Search code examples
ioslayoutsubviews

What kind of things can cause viewWillLayoutSubviews to be run in the background thread?


I have detected a rare crash in my application. The stack trace was not helpful. It is not directly related to my code:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSAttributeDictionary textContainerForAttributedString:containerSize:lineFragmentPadding:]: unrecognized selector sent to instance 0x1b8d89c0'

Last Exception Backtrace:
0   CoreFoundation                      0x2e23bfd3 __exceptionPreprocess + 131
1   libobjc.A.dylib                     0x38ab2ccf objc_exception_throw + 38
2   CoreFoundation                      0x2e23f967 -[NSObject(NSObject) doesNotRecognizeSelector:] + 202
3   CoreFoundation                      0x2e23e253 ___forwarding___ + 706
4   CoreFoundation                      0x2e18d7f8 __forwarding_prep_0___ + 24
5   UIFoundation                        0x35e77a25 __NSStringDrawingEngine + 12204
6   UIFoundation                        0x35e749a7 -[NSString(NSExtendedStringDrawing) drawWithRect:options:attributes:context:] + 150
7   UIKit                               0x30a70d21 -[UILabel _drawTextInRect:baselineCalculationOnly:] + 4224
8   UIKit                               0x30ad7009 -[UILabel drawTextInRect:] + 500
9   UIKit                               0x30ad6e0b -[UILabel drawRect:] + 78
10  UIKit                               0x30ad6da5 -[UIView(CALayerDelegate) drawLayer:inContext:] + 372
11  QuartzCore                          0x307042c1 -[CALayer drawInContext:] + 100
12  QuartzCore                          0x306ede3f CABackingStoreUpdate_ + 1854
13  QuartzCore                          0x307c8d6d ___ZN2CA5Layer8display_Ev_block_invoke + 52
14  QuartzCore                          0x306ed6f3 x_blame_allocations + 82
15  QuartzCore                          0x306ed39b CA::Layer::display_() + 1106
16  QuartzCore                          0x306d103d CA::Layer::display_if_needed(CA::Transaction*) + 208
17  QuartzCore                          0x306d0cd5 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 24
18  QuartzCore                          0x306d06df CA::Context::commit_transaction(CA::Transaction*) + 230
19  QuartzCore                          0x306d04ef CA::Transaction::commit() + 314
20  QuartzCore                          0x306fda83 CA::Transaction::release_thread(void*) + 162
21  libsystem_pthread.dylib             0x390dd68d _pthread_tsd_cleanup + 164
22  libsystem_pthread.dylib             0x390dd40b _pthread_exit + 86
23  libsystem_pthread.dylib             0x390de17d pthread_exit + 28
24  Foundation                          0x2eb7846f +[NSThread exit] + 10
25  Foundation                          0x2ec24a7d __NSThread__main__ + 1092
26  libsystem_pthread.dylib             0x390de919 _pthread_body + 140
27  libsystem_pthread.dylib             0x390de88b _pthread_start + 102
28  libsystem_pthread.dylib             0x390dcaa4 thread_start + 8

Therefore, I tried to add more debug information to find out more about the crash. I could find traces to viewWillLayoutSubviews. It is being called in the background. I can just exit from the function if I detect it is in the background. But this is a dirty hack. I want to fix it correctly by preventing "things" from making the app call viewWillLayoutSubviews in the background thread.

I couldn't yet found those "things" as stacktrace is not helpful.

frame #1: 0x32946d16 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 314
frame #2: 0x325c462a QuartzCore`-[CALayer layoutSublayers] + 142
frame #3: 0x325bfe3a QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 350
frame #4: 0x325bfccc QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 16
frame #5: 0x325bf6de QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 230
frame #6: 0x325bf4ee QuartzCore`CA::Transaction::commit() + 314
frame #7: 0x325eca82 QuartzCore`CA::Transaction::release_thread(void*) + 162
frame #8: 0x3af0568c libsystem_pthread.dylib`_pthread_tsd_cleanup + 164
frame #9: 0x3af0540a libsystem_pthread.dylib`_pthread_exit + 86
frame #10: 0x3af0617c libsystem_pthread.dylib`pthread_exit + 28
frame #11: 0x30a6746e Foundation`+[NSThread exit] + 10
frame #12: 0x30b13a7c Foundation`__NSThread__main__ + 1092
frame #13: 0x3af06918 libsystem_pthread.dylib`_pthread_body + 140
frame #14: 0x3af0688a libsystem_pthread.dylib`_pthread_start + 102`

What can be those "things"?


Solution

  • This kind of crash occurs when you call something in the background that must run on the main thread.

    Finding the exact line may be almost impossible. I found the result with lots of NSLog lines and [NSThread isMainThread] lines.

    Calling almost anything on UI elements (especially those require size calculations/changes, triggers viewWillLayoutSubviews. In my case it was both UIImageViews and UILabels.

    If you can't find where you make UI updates in the background, check if you post NSNotifications in the background.