神秘崩溃(NSAttributedString,iOS 7)

7
在我的应用程序中,我有一个自己编写的UICollectionView版本(在UICollectionView出现之前我就写了它,对于我的使用情况来说,它似乎比UICollectionView更高效)。它由滚动视图中的几个平铺组成。
为了帮助滚动性能,并且因为每个平铺由十几个PNG堆叠而成,每个平铺都被预先渲染到NSCache中,使用NSOperations和NSOperationQueue。如果没有这种优化,滚动性能会非常糟糕。
除了几个PNG外,每个平铺还有(或可能有)一些文本片段。
为了避免在后台线程上进行任何类型的渲染而导致问题(这当然是UIKit不允许的),每个NSOperation使用CoreGraphics和NSAttributedStrings来渲染图像。
这就带来了崩溃。下面的崩溃似乎与NSAttributedString渲染有关。由于这个充满平铺渲染操作的NSOperationQueue是我的应用程序中唯一使用NSAttributedStrings并经常被调用的地方,我必须假定它是罪魁祸首。我们在崩溃报告中经常看到崩溃发生,但我们很难在内部可靠地重现它(我们的应用程序有一个相当大的用户群)。
有趣的是,这种崩溃只会发生在iOS 7上。谁有任何想法?以下是一个崩溃示例:
Incident Identifier: 3BE1FF37-4288-40A9-954D-1AD909987D05
CrashReporter Key:   000339CB-9B5B-46EE-B256-4D911B190E7D
Hardware Model:      iPhone5,2
Process:         AppName [733]
Path:            /Users/USER/AppName.app/AppName
Identifier:      com.companyname.appname
Version:         2.0.1
Code Type:       ARM
Parent Process:  launchd [1]

Date/Time:       2013-10-03 23:32:20 +0000
OS Version:      iPhone OS 7.0.2 (11A501)
Report Version:  104

Exception Type:  SIGBUS
Exception Codes: BUS_ADRALN at 0x100078c
Crashed Thread:  0

Thread 0 Crashed:
0   ???                                  0x0100078c 0x0 + 0
1   UIFoundation                         0x36fbf027 -[NSLayoutManager _drawLineForGlyphRange:type:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:isStrikethrough:] + 3358
2   UIFoundation                         0x36ffdd1f -[NSLayoutManager drawUnderlineForGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:] + 86
3   UIFoundation                         0x36fc024d -[NSLayoutManager _lineGlyphRange:type:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:isStrikethrough:] + 1184
4   UIFoundation                         0x36ffde2f -[NSLayoutManager underlineGlyphRange:underlineType:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:] + 78
5   UIFoundation                         0x36fd43bf -[NSLayoutManager _drawGlyphsForGlyphRange:atPoint:] + 5734
6   UIFoundation                         0x36ffdbf7 -[NSLayoutManager drawGlyphsForGlyphRange:atPoint:] + 38
7   UIFoundation                         0x37013f01 -[NSStringDrawingTextStorage drawTextContainer:withRect:graphicsContext:baselineMode:scrollable:padding:] + 968
8   UIFoundation                         0x3700e5bd __NSStringDrawingEngine + 10293
9   UIFoundation                         0x3701172d -[NSAttributedString(NSExtendedStringDrawing) drawWithRect:options:context:] + 525
10  UIKit                                0x31ed9f5d -[UILabel _drawTextInRect:baselineCalculationOnly:] + 3436
11  UIKit                                0x31f419e7 -[UILabel drawTextInRect:] + 558
12  UIKit                                0x31f417af -[UILabel drawRect:] + 78
13  UIKit                                0x31f41749 -[UIView drawLayer:inContext:] + 372
14  QuartzCore                           0x31b78049 -[CALayer drawInContext:] + 100
15  QuartzCore                           0x31b61813 CABackingStoreUpdate_ + 1859
16  QuartzCore                           0x31c3b735 ___ZN2CA5Layer8display_Ev_block_invoke + 53
17  QuartzCore                           0x31b610c3 x_blame_allocations + 83
18  QuartzCore                           0x31b60d77 CA::Layer::display_() + 1119
19  QuartzCore                           0x31b44969 CA::Layer::display_if_needed(CA::Transaction*) + 209
20  QuartzCore                           0x31b44601 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 25
21  QuartzCore                           0x31b4400d CA::Context::commit_transaction(CA::Transaction*) + 229
22  QuartzCore                           0x31b43e1f CA::Transaction::commit() + 315
23  QuartzCore                           0x31b3db4d CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 57
24  CoreFoundation                       0x2f706f71 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 21
25  CoreFoundation                       0x2f7048ff __CFRunLoopDoObservers + 287
26  CoreFoundation                       0x2f704c4b __CFRunLoopRun + 739
27  CoreFoundation                       0x2f66f541 CFRunLoopRunSpecific + 524
28  CoreFoundation                       0x2f66f323 CFRunLoopRunInMode + 106
29  GraphicsServices                     0x343a62eb GSEventRunModal + 138
30  UIKit                                0x31f261e5 UIApplicationMain + 1136
31  My App                               0x0002cc37 main (main.m:18)
32  libdyld.dylib                        0x39f2fab7 start + 3

Thread 1:
0   libsystem_kernel.dylib               0x39fd3838 kevent64 + 24
1   libdispatch.dylib                    0x39f1c643 _dispatch_mgr_thread + 39

Thread 2:
0   libsystem_kernel.dylib               0x39fd3a84 mach_msg_trap + 20
1   CoreFoundation                       0x2f706561 __CFRunLoopServiceMachPort + 157
2   CoreFoundation                       0x2f704c81 __CFRunLoopRun + 793
3   CoreFoundation                       0x2f66f541 CFRunLoopRunSpecific + 524
4   CoreFoundation                       0x2f66f323 CFRunLoopRunInMode + 106
5   Foundation                           0x3005d827 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 255
6   Foundation                           0x300ae669 -[NSRunLoop(NSRunLoop) run] + 81
7   My App                          0x0003900f -[EndpointOperation start] (EndpointOperation.m:268)
8   Foundation                           0x3010eafd __NSOQSchedule_f + 61
9   libdispatch.dylib                    0x39f1f4bf _dispatch_async_redirect_invoke + 111
10  libdispatch.dylib                    0x39f207e5 _dispatch_root_queue_drain + 225
11  libdispatch.dylib                    0x39f209d1 _dispatch_worker_thread2 + 57
12  libsystem_pthread.dylib              0x3a04adff _pthread_wqthread + 298
13  libsystem_pthread.dylib              0x3a04acc4 start_wqthread + 8

Thread 3:
0   libsystem_kernel.dylib               0x39fd3a84 mach_msg_trap + 20
1   CoreFoundation                       0x2f706561 __CFRunLoopServiceMachPort + 157
2   CoreFoundation                       0x2f704c81 __CFRunLoopRun + 793
3   CoreFoundation                       0x2f66f541 CFRunLoopRunSpecific + 524
4   CoreFoundation                       0x2f66f323 CFRunLoopRunInMode + 106
5   Foundation                           0x300aa651 +[NSURLConnection _resourceLoadLoop:] + 320
6   Foundation                           0x3011fdc7 __NSThread__main__ + 1063
7   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
8   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
9   libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 4:
0   libsystem_kernel.dylib               0x39fd3a84 mach_msg_trap + 20
1   CoreFoundation                       0x2f706561 __CFRunLoopServiceMachPort + 157
2   CoreFoundation                       0x2f704c81 __CFRunLoopRun + 793
3   CoreFoundation                       0x2f66f541 CFRunLoopRunSpecific + 524
4   CoreFoundation                       0x2f66f323 CFRunLoopRunInMode + 106
5   Foundation                           0x3005d827 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 255
6   My App                          0x000a1287 +[XMPPStream xmppThreadMain] (XMPPStream.m:4463)
7   Foundation                           0x3011fdc7 __NSThread__main__ + 1063
8   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
9   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
10  libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 5:
0   libsystem_kernel.dylib               0x39fe6440 __select + 20
1   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
2   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
3   libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 6:
0   libsystem_kernel.dylib               0x39fd3a84 mach_msg_trap + 20
1   CoreFoundation                       0x2f706561 __CFRunLoopServiceMachPort + 157
2   CoreFoundation                       0x2f704c81 __CFRunLoopRun + 793
3   CoreFoundation                       0x2f66f541 CFRunLoopRunSpecific + 524
4   CoreFoundation                       0x2f66f323 CFRunLoopRunInMode + 106
5   WebCore                              0x375d27dd RunWebThread(void*) + 421
6   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
7   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
8   libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 7:
0   libsystem_kernel.dylib               0x39fd3a84 mach_msg_trap + 20
1   CoreFoundation                       0x2f706561 __CFRunLoopServiceMachPort + 157
2   CoreFoundation                       0x2f704c81 __CFRunLoopRun + 793
3   CoreFoundation                       0x2f66f541 CFRunLoopRunSpecific + 524
4   CoreFoundation                       0x2f66f323 CFRunLoopRunInMode + 106
5   libAVFAudio.dylib                    0x2e64d5b3 GenericRunLoopThread::Entry(void*) + 131
6   libAVFAudio.dylib                    0x2e641bf7 CAPThread::Entry(CAPThread*) + 179
7   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
8   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
9   libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 8:
0   libsystem_kernel.dylib               0x39fe5f38 __psynch_cvwait + 24
1   libsystem_pthread.dylib              0x3a04d041 pthread_cond_wait + 40
2   JavaScriptCore                       0x3069340d JSC::BlockAllocator::blockFreeingThreadMain() + 209
3   JavaScriptCore                       0x30690a73 WTF::wtfThreadEntryPoint(void*) + 15
4   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
5   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
6   libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 9:
0   libsystem_kernel.dylib               0x39fe5f38 __psynch_cvwait + 24
1   libsystem_pthread.dylib              0x3a04d041 pthread_cond_wait + 40
2   JavaScriptCore                       0x30831af7 JSC::GCThread::waitForNextPhase() + 79
3   JavaScriptCore                       0x30831b51 JSC::GCThread::gcThreadMain() + 53
4   JavaScriptCore                       0x30690a73 WTF::wtfThreadEntryPoint(void*) + 15
5   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
6   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
7   libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 10:
0   libsystem_kernel.dylib               0x39fd3a84 mach_msg_trap + 20
1   CoreFoundation                       0x2f706561 __CFRunLoopServiceMachPort + 157
2   CoreFoundation                       0x2f704c81 __CFRunLoopRun + 793
3   CoreFoundation                       0x2f66f541 CFRunLoopRunSpecific + 524
4   CoreFoundation                       0x2f66f323 CFRunLoopRunInMode + 106
5   Foundation                           0x3005d827 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 255
6   Foundation                           0x300ae669 -[NSRunLoop(NSRunLoop) run] + 81
7   My App                          0x000b1635 +[GCDAsyncSocket cfstreamThread] (GCDAsyncSocket.m:6714)
8   Foundation                           0x3011fdc7 __NSThread__main__ + 1063
9   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
10  libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
11  libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 11:
0   libsystem_kernel.dylib               0x39fd3a84 mach_msg_trap + 20
1   CoreFoundation                       0x2f706561 __CFRunLoopServiceMachPort + 157
2   CoreFoundation                       0x2f704c81 __CFRunLoopRun + 793
3   CoreFoundation                       0x2f66f541 CFRunLoopRunSpecific + 524
4   CoreFoundation                       0x2f6b31ab CFRunLoopRun + 98
5   CoreMotion                           0x2fd27399 CLSF_thorntonUpdate_6x6 + 57225
6   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
7   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
8   libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 12:
0   libsystem_kernel.dylib               0x39fd3ad4 semaphore_wait_trap + 8
1   MediaToolbox                         0x30b60d0f fpa_AsyncMovieControlThread + 1755
2   CoreMedia                            0x2fc9b23f figThreadMain + 195
3   libsystem_pthread.dylib              0x3a04cc5d _pthread_body + 141
4   libsystem_pthread.dylib              0x3a04cbcf _pthread_start + 102
5   libsystem_pthread.dylib              0x3a04acd0 thread_start + 8

Thread 13:
0   libsystem_kernel.dylib               0x39fe6c7c __workq_kernreturn + 8
1   libsystem_pthread.dylib              0x3a04acc4 start_wqthread + 8

Thread 14:
0   libsystem_kernel.dylib               0x39fe6c7c __workq_kernreturn + 8
1   libsystem_pthread.dylib              0x3a04acc4 start_wqthread + 8

Thread 15:
0   libsystem_kernel.dylib               0x39fe6c7c __workq_kernreturn + 8
1   libsystem_pthread.dylib              0x3a04acc4 start_wqthread + 8

Thread 16:
0   libsystem_kernel.dylib               0x39fe6c7c __workq_kernreturn + 8
1   libsystem_pthread.dylib              0x3a04acc4 start_wqthread + 8

Thread 0 crashed with ARM Thread State:
    pc: 0x0100078c     r7: 0x27ddb5e8     sp: 0x27ddb5b8     r0: 0x16d20b30 
    r1: 0x27ddb5b8     r2: 0x0100078c     r3: 0x00000000     r4: 0x169a6d78 
    r5: 0x16b33250     r6: 0x2fe140a5     r8: 0x27ddb5b8     r9: 0x164ea8b8 
   r10: 0x157ea030    r11: 0x00000004     ip: 0x3a265964     lr: 0x2fe1402d 
  cpsr: 0x40000010 

这里是代码:

NSMutableDictionary *attributes = [@{NSFontAttributeName: [[self class] unreadChatLabelFont]
                                    , NSForegroundColorAttributeName: [UIColor whiteColor]} mutableCopy];

if ([self unreadChatLabelHasDropShadow]) {

    NSShadow *shadow = [[NSShadow alloc] init];
    shadow.shadowOffset = CGSizeMake(0, -1);
    shadow.shadowColor = [UIColor colorWithRed: 0 green: 0 blue: 0 alpha: .5];
    attributes[NSShadowAttributeName] = shadow;

}



NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString: unreadChats
                                                                       attributes: attributes];

[attributedString drawAtPoint: labelPosition];

同一课程的不同部分中:

(涉及IT技术)
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetShadowWithColor(context, CGSizeMake(0,1), 1.5, [UIColor blackColor].CGColor);

NSDictionary *attributes = @{NSFontAttributeName: [[self class] displayNameFont], NSForegroundColorAttributeName: [UIColor whiteColor]};

NSAttributedString *displayName = [[NSAttributedString alloc] initWithString: dictionary[kDisplayNameKey]
                                                                  attributes: attributes];

[displayName drawAtPoint: [[self class] displayNamePosition]];

// Remove shadow from draw settings
CGContextSetShadowWithColor(context, CGSizeMake(0,1), 1.5, NULL);

具体来说,descender_leadingunderlineThicknessisVerticalascender。你不是在子类化UIFont,重写它们并做一些疯狂的事情吧? - iccir
很遗憾 - 这些方法中仅包含 return [UIFont boldSystemFontOfSize: 11] 或 return [UIFont systemFontOfSize: 12]。 - Nick Locking
1
好吧,这是一个冒险尝试 ;) 所以,我刚刚注意到这个:“为了避免在后台线程上执行任何渲染(当然,这是UIKit不允许的),每个NSOperation都使用CoreGraphics和NSAttributedStrings来渲染图像。”NSAttributedString只是一个存储类。如果您正在使用-drawAtPoint:或-drawInRect:,或者如果您的NSAttributedString包含UIKit类,则仍在使用UIKit。虽然UIKit绘图应该在iOS 4中变得线程安全,但iOS 7中的文本系统完全是新的,如果它存在线程安全漏洞,那也不足为奇。 - iccir
2
根据iOS的参考文档:“ 只要应用程序保证从单个线程访问,就可以从子线程访问NLayoutManager、NSTextStorage和NSTextContainer。” 还可以查看apple.com/library/mac/documentation/cocoa/conceptual/TextLayout/Concepts/LayoutManager.html。即使这是一个OS X文档,iOS的NSLayoutManager代码也源自OS X版本。 - iccir
1
@RyanR 嗯,是的,可能是这个原因。根据回溯信息,NSAttributedString 可能会保留一个共享的 NSLayoutManager 用于绘制,而在你自己的线程中调用 drawAtPoint: 可能会干扰主线程中常规 UILabel 等的绘制。 - Carl Lindberg
显示剩余4条评论
3个回答

2
请确保您不会添加标签、更改标签中的文本或在主线程以外的线程上绘制文本。虽然文档中说过以下内容:
NSTextStorage、NSLayoutManager 和 NSTextContainer 可以从子线程访问,只要应用程序保证从单个线程访问。
但我在过去发现它们并不完全线程安全,一些与字形有关的操作必须在主线程上进行,否则会产生异常或崩溃,并出现EXC_BAD_ACCESS。虽然这不完全像您的崩溃报告,但这是一个可能的崩溃原因。

1
为了排除跨线程UIKit绘制的问题,请将您的调用分派到主队列上的-drawAtPoint:
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:unreadChats attributes:attributes];

dispatch_async(dispatch_get_main_queue(), ^{
  [attributedString drawAtPoint: labelPosition];
});

0
我看到的第一件事是你没有设置shadowBlurRadius。手册上说:

此属性包含模糊半径,以默认用户坐标空间中的度量为准。值为0表示无模糊,而较大的值会产生相应更大的模糊效果。默认值为0。

我建议尝试使用非零值进行设置,看看是否可以解决崩溃问题。

一个 shadowBlurRadius 为0是完全合法的。它通常与非0的偏移量一起使用,以制作轻微的阴影或内插效果。 - iccir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接