Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UICollectionView strange crash in iOS 7

Im getting a strange crash inside UICollectionView using ios 7 which tends to be happening when scrolling very fast. A similar issue has been reported on the Apple Developer Forms here: https://devforums.apple.com/message/901009#901009

I'm wondering if anyone out there has experienced this and found a fix/workaround?

I'm using a basic UICollection view configured with flow layout. It's being populated from json data over the network. The data periodically changes. When it does the reloadData method is called. I have checked to make sure the reload method is always called on the main thread, as I was worried that one of my network callbacks was calling back on a background thread. This is definitely not the case.

When turning on NSZmobies I get the following log message when the crash occurs:

*** -[NSIndexPath section]: message sent to deallocated instance 0x218b74c0

I also ran my app attached to the Instruments using the Zombies trace template and was able to attain the following stack trace detailing the sequence of calls leading to the crash.

Note all the calls are iOS framework level calls and not my application calls.

   0 libsystem_malloc.dylib malloc_zone_calloc
   1 libsystem_malloc.dylib calloc
   2 libobjc.A.dylib class_createInstance
   3 libobjc.A.dylib +[NSObject allocWithZone:]
   4 Foundation +[NSIndexPath indexPathWithIndexes:length:]
   5 UIKit +[NSIndexPath(UITableView) indexPathForRow:inSection:]
   6 UIKit -[UICollectionViewFlowLayout _layoutAttributesForItemsInRect:]
   7 UIKit -[UICollectionViewFlowLayout layoutAttributesForElementsInRect:]
   8 UIKit __45-[UICollectionViewData validateLayoutInRect:]_block_invoke
   9 UIKit -[UICollectionViewData validateLayoutInRect:]
  10 UIKit -[UICollectionView layoutSubviews]
  11 UIKit -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
  12 QuartzCore -[CALayer layoutSublayers]
  13 QuartzCore CA::Layer::layout_if_needed(CA::Transaction*)
  14 QuartzCore CA::Layer::layout_and_display_if_needed(CA::Transaction*)
  15 QuartzCore CA::Context::commit_transaction(CA::Transaction*)
  16 QuartzCore CA::Transaction::commit()
  17 QuartzCore CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*)
  18 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
  19 CoreFoundation __CFRunLoopDoObservers
  20 CoreFoundation CFRunLoopRunSpecific
  21 CoreFoundation CFRunLoopRunInMode
  22 GraphicsServices GSEventRunModal
  23 UIKit UIApplicationMain
  24 Mixlr main /Users/saman/Desktop/mixlr-iphone/Mixlr/main.m:14
  25 libdyld.dylib start
like image 793
Sabobin Avatar asked Oct 18 '13 11:10

Sabobin


1 Answers

If the user is actively scrolling, the collection is actively requesting cells and headers. So while this is happening, you have modified the backing data set and called reloadData, but there is a window where the data has been changed but reloadData not yet issued.

If correct, then the solution is to create a change set that only gets applied when the user is not actively scrolling.

Also using reloadData is using a big hammer on a small nail - if possible insert, delete, or update individual cells and views.

EDIT: this situation is quite similar to what people are experiencing with UITableViews. Note that reloadData returns immediately - the CollectionView just queues the reload and schedules it to run at the end of the current runloop cycle (not sure how they do this, but by sending dispatch_async NSLogs you can verify it does in fact to it).

What I'm seeing in my collection view is that during active scrolling I'm getting main thread messages as cells are created, then the reload data call, then didDisplay delegate messages for the current set of viewable cells, then reloadData returns, then it starts asking for the new set (which I made much smaller to see if I could get your crash).

I guess the best advice I can give you at this time is to wrap the data model changes then a reloadData message up in a dispatch block, and send all at the same time (or do all in a single method.

like image 58
David H Avatar answered Nov 16 '22 01:11

David H