2012-10-05 12 views
22

में थ्रेड सुरक्षित प्रतीत नहीं होता है, दो धागे पर किसी भी UIStringDrawing विधियों का उपयोग एक साथ दुर्घटना का कारण बनता है। मेरी समझ यह थी कि आईओएस 4.0 से सभी UIStringDrawing विधियां सुरक्षित थीं।UIStringDrawing विधियों को आईओएस 6

इस कोड (जो किसी काम के कुछ नहीं करता है) समस्या को दर्शाता है:

* thread #1: tid = 0x2403, 0x00ad40c8, stop reason = EXC_BAD_ACCESS (code=2, address=0xad40c8) 
    frame #0: 0x00ad40c8 
    frame #1: 0x36bc4252 WebCore`WebCore::Font::Font(WebCore::FontPlatformData const&, WTF::PassRefPtr<WebCore::FontSelector>) + 90 
    frame #2: 0x36bc41f2 WebCore`WebCore::Font::Font(WebCore::FontPlatformData const&, WTF::PassRefPtr<WebCore::FontSelector>) + 10 
    frame #3: 0x38f0368e WebKit`rendererForFont(__GSFont*) + 246 
    frame #4: 0x38f03230 WebKit`-[NSString(WebStringDrawing) _web_sizeWithFont:forWidth:ellipsis:letterSpacing:resultRange:] + 200 
    frame #5: 0x38f03162 WebKit`-[NSString(WebStringDrawing) _web_sizeWithFont:forWidth:ellipsis:letterSpacing:] + 66 
    frame #6: 0x38f04532 WebKit`-[NSString(WebStringDrawing) _web_sizeWithFont:] + 58 
    frame #7: 0x361dc5d2 UIKit`-[NSString(UIStringDrawing) sizeWithFont:] + 46 
    frame #8: 0x00060ca8 myApp`-[TAViewController drawingTest] + 216 at TAViewController.m:157 
    frame #9: 0x38da1e66 Foundation`__NSFireDelayedPerform + 450 
    frame #10: 0x3aa47856 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 14 
    frame #11: 0x3aa47502 CoreFoundation`__CFRunLoopDoTimer + 274 
    frame #12: 0x3aa46176 CoreFoundation`__CFRunLoopRun + 1230 
    frame #13: 0x3a9b923c CoreFoundation`CFRunLoopRunSpecific + 356 
    frame #14: 0x3a9b90c8 CoreFoundation`CFRunLoopRunInMode + 104 
    frame #15: 0x3a8a433a GraphicsServices`GSEventRunModal + 74 
    frame #16: 0x3622c288 UIKit`UIApplicationMain + 1120 
    frame #17: 0x0005f08c myApp`main + 96 at main.m:16 

    thread #5: tid = 0x2a03, 0x00ad40c8, stop reason = EXC_BAD_ACCESS (code=2, address=0xad40c8) 
    frame #0: 0x00ad40c8 
    frame #1: 0x36bc4252 WebCore`WebCore::Font::Font(WebCore::FontPlatformData const&, WTF::PassRefPtr<WebCore::FontSelector>) + 90 
    frame #2: 0x36bc41f2 WebCore`WebCore::Font::Font(WebCore::FontPlatformData const&, WTF::PassRefPtr<WebCore::FontSelector>) + 10 
    frame #3: 0x38f0368e WebKit`rendererForFont(__GSFont*) + 246 
    frame #4: 0x38f03230 WebKit`-[NSString(WebStringDrawing) _web_sizeWithFont:forWidth:ellipsis:letterSpacing:resultRange:] + 200 
    frame #5: 0x38f03162 WebKit`-[NSString(WebStringDrawing) _web_sizeWithFont:forWidth:ellipsis:letterSpacing:] + 66 
    frame #6: 0x38f04532 WebKit`-[NSString(WebStringDrawing) _web_sizeWithFont:] + 58 
    frame #7: 0x361dc5d2 UIKit`-[NSString(UIStringDrawing) sizeWithFont:] + 46 
    frame #8: 0x00060d5c myApp`__31-[TAViewController drawingTest]_block_invoke_0 + 116 at TAViewController.m:150 
    frame #9: 0x339f0792 libdispatch.dylib`_dispatch_call_block_and_release + 10 
    frame #10: 0x339f3b3a libdispatch.dylib`_dispatch_queue_drain + 142 
    frame #11: 0x339f167c libdispatch.dylib`_dispatch_queue_invoke + 44 
    frame #12: 0x339f4612 libdispatch.dylib`_dispatch_root_queue_drain + 210 
    frame #13: 0x339f47d8 libdispatch.dylib`_dispatch_worker_thread2 + 92 
    frame #14: 0x37f957f0 libsystem_c.dylib`_pthread_wqthread + 360 
    frame #15: 0x37f95684 libsystem_c.dylib`start_wqthread + 8 

मेरे समझ गया था:

dispatch_queue_t queue = dispatch_queue_create("com.queue", NULL); 

for (int i = 0; i < 10000; i++) { 

    dispatch_async(queue, ^{ 

     NSString *string = @"My string"; 
     CGSize size = [string sizeWithFont:[UIFont boldSystemFontOfSize:13]]; 
    }); 
} 

for (int i = 0; i < 10000; i++) { 

    NSString *string = @"My string"; 
    CGSize size = [string sizeWithFont:[UIFont boldSystemFontOfSize:13]]; 
} 

dispatch_release(queue); 

निम्नलिखित पश्व-अनुरेखन साथ छोरों के कुछ पुनरावृत्तियों के बाद ऐप्लिकेशन क्रैश कि UIStringDrawing विधियां आईओएस 4 से थ्रेड सुरक्षित थीं। मुझे उम्मीद है कि इन लूपों को कोई त्रुटि नहीं होनी चाहिए।

आईफोन 6 पर चलने वाले आईफोन पर चलने पर क्रैश होता है (आईफोन 5 पर परीक्षण किया जाता है) लेकिन आईओएस 5 (आईफोन 4 पर परीक्षण) या सिम्युलेटर (आईओएस 6 के साथ परीक्षण) चलाने पर आईफोन पर चलते समय ऐसा नहीं होता है।

मैं लागू कर दिया है कि मैं क्या सोचा था कि किसी भी ड्रा serialising द्वारा एक फिक्स था CGD का उपयोग कर कॉल:

- (void)serialiseDrawing:(void (^)())block { 

    dispatch_sync(self.serialDrawingQueue, block); 
} 


- (dispatch_queue_t)serialDrawingQueue { 

    if (_serialDrawingQueue == NULL) _serialDrawingQueue = dispatch_queue_create("com.myApp.SerialDrawQueue", NULL); 

    return _serialDrawingQueue; 
} 

... और लपेटकर हर आकर्षित इस तरह कहते हैं:

__block CGSize labelSize = CGSizeZero; 

[[TAUtils sharedUtils] serialiseDrawing:^{ 
    labelSize = [label.text sizeWithFont:label.font]; 
}]; 

यह है करने के लिए लगता है कुछ चीजों में सुधार हुआ (मेरी सभी UIStringDrawing कॉल एक थ्रेड पर होती है)। लेकिन यह अभी भी इस तरह की एक पश्व-अनुरेखन साथ समय पर दुर्घटना होगा:

Exception Type: EXC_CRASH (SIGSEGV) 
Exception Codes: 0x0000000000000000, 0x0000000000000000 
Crashed Thread: 0 

Thread 0 name: Dispatch queue: com.apple.main-thread 
Thread 0 Crashed: 
0 libsystem_kernel.dylib   0x3a28ee80 semaphore_wait_trap + 8 
1 libdispatch.dylib    0x32851e90 _dispatch_thread_semaphore_wait + 8 
2 libdispatch.dylib    0x32850680 _dispatch_barrier_sync_f_slow + 100 
3 myApp       0x000c4330 -[TAUtils serialiseDrawing:] (TAUtils.m:305) 
4 myApp       0x000edfd4 -[TAOmniBar updateLabel] (TAOmniBar.m:394) 
5 myApp       0x000ee8d6 -[TAOmniBar handleNotification:] (TAOmniBar.m:461) 
6 CoreFoundation     0x39820346 _CFXNotificationPost + 1418 
7 Foundation      0x37b5838a -[NSNotificationCenter postNotificationName:object:userInfo:] + 66 
8 Foundation      0x37b5be9a -[NSNotificationCenter postNotificationName:object:] + 26 
9 myApp       0x000f369a -[TAMyViewController update] (TAMyViewController.m:1308) 
10 GLKit       0x328383ce -[GLKViewController _updateAndDraw] + 270 
11 QuartzCore      0x39ffd77c CA::Display::DisplayLink::dispatch(unsigned long long, unsigned long long) + 156 
12 QuartzCore      0x39ffd6d4 CA::Display::IOMFBDisplayLink::callback(__IOMobileFramebuffer*, unsigned long long, unsigned long long, unsigned long long, void*) + 60 
13 IOMobileFramebuffer    0x31221fd4 IOMobileFramebufferVsyncNotifyFunc + 152 
14 IOKit       0x39f7c5aa IODispatchCalloutFromCFMessage + 190 
15 CoreFoundation     0x39899888 __CFMachPortPerform + 116 
16 CoreFoundation     0x398a43e4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32 
17 CoreFoundation     0x398a4386 __CFRunLoopDoSource1 + 134 
18 CoreFoundation     0x398a320a __CFRunLoopRun + 1378 
19 CoreFoundation     0x39816238 CFRunLoopRunSpecific + 352 
20 CoreFoundation     0x398160c4 CFRunLoopRunInMode + 100 
21 GraphicsServices    0x39701336 GSEventRunModal + 70 
22 UIKit       0x35089284 UIApplicationMain + 1116 
23 myApp       0x000b806e main (main.m:16) 
24 myApp       0x000b8024 start + 36 

Thread 7 name: Dispatch queue: com.myApp.SerialDrawQueue 
Thread 7: 
0 WebCore       0x35a21410 WebCore::FontFallbackList::invalidate(WTF::PassRefPtr<WebCore::FontSelector>) + 156 
1 WebCore       0x35a2124e WebCore::Font::Font(WebCore::FontPlatformData const&, WTF::PassRefPtr<WebCore::FontSelector>) + 86 
2 WebCore       0x35a211ee WebCore::Font::Font(WebCore::FontPlatformData const&, WTF::PassRefPtr<WebCore::FontSelector>) + 6 
3 WebKit       0x37d6068a rendererForFont(__GSFont*) + 242 
4 WebKit       0x37d61796 -[NSString(WebStringDrawing) __web_drawAtPoint:forWidth:withFont:ellipsis:letterSpacing:includeEmoji:measureOnly:renderedStringOut:drawUnderline:] + 198 
5 WebKit       0x37d616bc -[NSString(WebStringDrawing) __web_drawAtPoint:forWidth:withFont:ellipsis:letterSpacing:includeEmoji:measureOnly:renderedStringOut:] + 84 
6 WebKit       0x37d6165e -[NSString(WebStringDrawing) __web_drawAtPoint:forWidth:withFont:ellipsis:letterSpacing:includeEmoji:measureOnly:] + 82 
7 WebKit       0x37d61602 -[NSString(WebStringDrawing) _web_drawAtPoint:forWidth:withFont:ellipsis:letterSpacing:includeEmoji:] + 78 
8 UIKit       0x35041960 -[NSString(UIStringDrawing) drawAtPoint:forWidth:withFont:lineBreakMode:letterSpacing:includeEmoji:] + 172 
9 UIKit       0x3507de1e -[NSString(UIStringDrawing) drawAtPoint:forWidth:withFont:fontSize:lineBreakMode:baselineAdjustment:includeEmoji:] + 358 
10 UIKit       0x3507dca4 -[NSString(UIStringDrawing) drawAtPoint:forWidth:withFont:fontSize:lineBreakMode:baselineAdjustment:] + 68 
11 myApp       0x000d3300 -[TALabelManager textureCGImageForString:] (TALabelManager.m:859) 
12 myApp       0x000d350a __39-[TALabelManager textureDataForString:]_block_invoke_0 (TALabelManager.m:875) 
13 libdispatch.dylib    0x3284d5d8 _dispatch_client_callout + 20 
14 libdispatch.dylib    0x3285080a _dispatch_barrier_sync_f_invoke + 22 
15 myApp       0x000c4330 -[TAUtils serialiseDrawing:] (TAUtils.m:305) 
16 myApp       0x000d3420 -[TALabelManager textureDataForString:] (TALabelManager.m:873) 
17 myApp       0x000d0dde __block_global_0 (TALabelManager.m:516) 
18 libdispatch.dylib    0x3284d790 _dispatch_call_block_and_release + 8 
19 libdispatch.dylib    0x32850b36 _dispatch_queue_drain + 138 
20 libdispatch.dylib    0x3284e678 _dispatch_queue_invoke + 40 
21 libdispatch.dylib    0x32851610 _dispatch_root_queue_drain + 208 
22 libdispatch.dylib    0x328517d4 _dispatch_worker_thread2 + 88 
23 libsystem_c.dylib    0x36df27ee _pthread_wqthread + 358 
24 libsystem_c.dylib    0x36df2680 start_wqthread + 4 

मैं लंबे समय से प्रश्न के लिए क्षमा चाहते हैं लेकिन यह मेरे लिए एक गंभीर समस्या है और वास्तव में किसी भी मदद की सराहना करेंगे।

+0

मैंने आपका पूरा प्रश्न नहीं पढ़ा लेकिन आप जानते हैं कि सभी चित्र हमेशा मुख्य धागे पर किया जाना चाहिए, है ना? इनमें से कोई भी धागा सुरक्षित नहीं है। –

+3

[तकनीकी क्यू एंड ए क्यूए 1637: कैटिल्डलेयर और यूआईकिट ग्राफिक्स] से (http://developer.apple.com/library/ios/#qa/qa1637/_index.html):> महत्वपूर्ण: आईओएस 4.0 के साथ शुरुआत, ग्राफिक्स संदर्भ में ड्राइंग UIKit में थ्रेड-सुरक्षित है। इसमें वर्तमान ग्राफिक्स स्टैक तक पहुंचने और छेड़छाड़ करने, छवियों और तारों को चित्रित करने, और माध्यमिक धागे से रंग और फ़ॉन्ट ऑब्जेक्ट्स का उपयोग शामिल है। –

+0

मुझे यह नहीं पता था। कुछ नया सीखना अच्छा लगता है –

उत्तर

22

जबकि चारों ओर मैंने देखा है कि IOS 6 NSAttributedString और कोर पाठ के बहुत अधिक व्यापक एकीकरण का परिचय तो मैं NSString के स्थान पर NSAttributedString का उपयोग कर बराबर NSStringDrawing तरीकों के साथ सभी UIStringDrawing तरीकों की अदला-बदली करने की कोशिश की एक काम खोजने की कोशिश और ऐसा लगता दुर्घटनाओं है रोका हुआ।

उदाहरण के लिए, मैं अब उपयोग कर रहा हूँ:

NSAttributedString *attribStr = [[NSAttributedString alloc] initWithString:@"My String"]; 
CGSize size = [attribStr size]; 

के बजाय:

NSString *str = @"My String"; 
CGSize size = [str sizeWithFont:font]; 
+0

मैं [t drawAtPoint: point withFont: font] का उपयोग कर रहा हूं, मैं इसका उपयोग कैसे कर सकता हूं - या कोई अन्य विधि - यदि मैं NSStringedString के साथ NSString को प्रतिस्थापित करता हूं? – user836026

+0

@ user836026 [NSAttributedString UIKit Additions] (http://developer.apple.com/library/ios/#documentation/uikit/reference/NSAttributedString_UIKit_Additions/Reference/Reference.html) 'drawAtPoint:' विधि प्रदान करता है। आपको फ़ॉन्ट निर्दिष्ट करने की आवश्यकता नहीं है क्योंकि इसे 'NSFontAttributeName' के साथ' NSAttributedString' में शामिल किया जाएगा। –

+0

मैं एक समान समस्या में भाग गया जहां मुझे पृष्ठभूमि थ्रेड पर 'UILabel' का उपयोग करते समय पूरी तरह से 'WebCore :: FontFallbackList :: अवैध (WTF :: PassRefPtr )' त्रुटि मिली। जब मैं '-justjustsFontSizeToFitWidth' का उपयोग करना बंद कर दिया तो त्रुटि दूर हो गई। मैं लेबल के लिए मैन्युअल रूप से फ़ॉन्ट आकार देने के लिए समाप्त हो गया। :( –

4

एडम सही है। UIStringDrawing विधियां केवल आईओएस 6 पर मुख्य कतार से उपयोग करने के लिए सुरक्षित हैं। आप या तो पृष्ठभूमि कतारों से प्रतिपादन करने के लिए सीधे NSStringDrawing विधियों या CoreText का उपयोग कर सकते हैं। यह एक ज्ञात मुद्दा है, लेकिन अधिक बग फ़ाइल करने के लिए स्वतंत्र महसूस करें।

3

एडम स्विंडेन का समाधान मेरे लिए काम करता था।

क्या हुआ करता था:

NSString *text = ...; 
CGFloat width = ...; 
UIFont *font = ...; 
CGSize size = [text sizeWithFont:font 
       constrainedToSize:(CGSize){width, CGFLOAT_MAX}]; 

साथ प्रतिस्थापित किया जा सकता है:

NSString *text = ...; 
CGFloat width = ...; 
UIFont *font = ...; 
NSAttributedString *attributedText = 
    [[NSAttributedString alloc] 
     initWithString:text 
     attributes:@ 
     { 
      NSFontAttributeName: font 
     }]; 
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX} 
              options:NSStringDrawingUsesLineFragmentOrigin 
              context:nil]; 
CGSize size = rect.size; 

कृपया प्रलेखन ध्यान दें का उल्लेख है:

आईओएस 7 में यहाँ कैसे मैं परिवर्तित NSString के sizeWithFont:constrainedToSize: है और बाद में, यह विधि आंशिक आकार लौटाती है (लौटा सीजीआरई के आकार घटक में सीटी); लौटा आकार का आकार विचारों का उपयोग करने के लिए, आपको छत समारोह का उपयोग करके निकटतम उच्च पूर्णांक पर अपना मान बढ़ाने का उपयोग करना होगा।

तो गणना ऊंचाई या चौड़ाई विचारों को आकार देने के लिए इस्तेमाल किया जाएगा बाहर निकलने के लिए, मैं का प्रयोग करेंगे:

CGFloat height = ceilf(size.height); 
CGFloat width = ceilf(size.width); 
+0

लेकिन यह आईओएस 6 के साथ काम नहीं करता है, है ना? – Raptor

+1

यह आईओएस 6 में काम करता है। मैंने एक ही सवाल को एक और सवाल पर दोबारा पोस्ट किया। मैं एक और विस्तृत स्पष्टीकरण देता हूं। एक नज़र डालें: http://stackoverflow.com/एक/18951386/588253 –

1

के आधार पर एडम Swinden की और श्री टी के जवाब मैंने लिखा 2 ड्रॉप-में तरीके:

@implementation NSString (Extensions) 

- (CGSize)threadSafeSizeWithFont:(UIFont *)font { 
    return [self threadSafeSizeWithFont:font constrainedToSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; 
} 

- (CGSize)threadSafeSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size { 
    // http://stackoverflow.com/questions/12744558/uistringdrawing-methods-dont-seem-to-be-thread-safe-in-ios-6 
    NSAttributedString *attributedText = 
    [[NSAttributedString alloc] 
    initWithString:self 
    attributes:@ 
    { 
    NSFontAttributeName: font 
    }]; 
    CGRect rect = [attributedText boundingRectWithSize:size 
               options:NSStringDrawingUsesLineFragmentOrigin 
               context:nil]; 
    return rect.size; 
} 

@end