Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UICollectionViewData updateItemCounts Crash

I'm getting crashes from deep inside Apple's layout code for a UICollectionViewFlowLayout and I have no idea how to address these (stack trace below). Any suggestions would be greatly appreciated.

Details:

  • Unfortunately I can't reproduce the issue
  • The issue isn't frequent (it happens in less than 0.1% of sessions) but it's our #1 crash
#0. Crashed: com.apple.main-thread
0  libsystem_platform.dylib       0x1879490d4 __bzero + 36
1  UIKit                          0x18e824e40 -[UICollectionViewData _updateItemCounts] + 544
2  UIKit                          0x18e824e40 -[UICollectionViewData _updateItemCounts] + 544
3  UIKit                          0x18e8e67e0 -[UICollectionViewData numberOfSections] + 28
4  UIKit                          0x18f086bc0 -[UICollectionViewFlowLayout _getSizingInfosWithExistingSizingDictionary:] + 612
5  UIKit                          0x18f088834 -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] + 152
6  UIKit                          0x18e8e66b4 -[UICollectionViewFlowLayout prepareLayout] + 224
7  UIKit                          0x18e7cf574 -[UICollectionViewData _prepareToLoadData] + 164
8  UIKit                          0x18e7ceb5c -[UICollectionViewData validateLayoutInRect:] + 100
9  UIKit                          0x18e7ce55c -[UICollectionView layoutSubviews] + 212
10 UIKit                          0x18e76fa80 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1196
11 QuartzCore                     0x18bc1d9d8 -[CALayer layoutSublayers] + 148
12 QuartzCore                     0x18bc124cc CA::Layer::layout_if_needed(CA::Transaction*) + 292
13 QuartzCore                     0x18bc1238c CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 32
14 QuartzCore                     0x18bb8f3e0 CA::Context::commit_transaction(CA::Transaction*) + 252
15 QuartzCore                     0x18bbb6a68 CA::Transaction::commit() + 512
16 QuartzCore                     0x18bbb7488 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 120
17 CoreFoundation                 0x18886a0c0 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
18 CoreFoundation                 0x188867cf0 __CFRunLoopDoObservers + 372
19 CoreFoundation                 0x188868180 __CFRunLoopRun + 1024
20 CoreFoundation                 0x1887962b8 CFRunLoopRunSpecific + 444
21 GraphicsServices               0x18a24a198 GSEventRunModal + 180
22 UIKit                          0x18e7dd7fc -[UIApplication _run] + 684
23 UIKit                          0x18e7d8534 UIApplicationMain + 208
24 AppName Mobile                 0x1000333e8 main (main.m:16)
25 libdispatch.dylib              0x1877795b8 (Missing)
like image 431
scosman Avatar asked Oct 18 '22 19:10

scosman


2 Answers

We finally found this one. It wasn't the performBatchUpdates issue reported on a lot of other cards. The root cause was one our data sources would return a negative number for item count. The SDK is using an unsigned int, so setting a negative rolled over, and would try to allocate billions of cells (34 GB of virtual memory and counting). It would eventually crash.

like image 151
scosman Avatar answered Oct 21 '22 11:10

scosman


There is a rampant problem with item counts and performBatchUpdates. Specifically, you may run into discrepancies and an OS assert if these are not in sync. An assert is not a crash.

I do suspect that this is what you encountered here, and if so, this question may be a duplicate of:

  • Nightmare with performBatchUpdates crash
  • How to solve this CollectionView crash
  • UICollectionView performBatchUpdates crash
  • crashing at performBatchUpdates of collection view

Assert or not, a defensive approach is to guard the item count as excellently outlined in Fang-Pen Lin's(†)UICollectionView invalid number of items crash problem and solution blog post:

func updateItems(updates: [ItemUpdate]) {
    collectionView.performBatchUpdates({
        for update in updates {
            switch update {
            case .Add(let index):
              collectionView.insertItemsAtIndexPaths([NSIndexPath(forItem: index, inSection: 0)])
                itemCount += 1
            case .Delete(let index):
              collectionView.deleteItemsAtIndexPaths([NSIndexPath(forItem: index, inSection: 0)])
                itemCount -= 1
            }
        }
    }, completion: nil)
}

Full Credit

like image 25
SwiftArchitect Avatar answered Oct 21 '22 11:10

SwiftArchitect