lionheart / openradar-mirror

A mirror of radars pulled from http://openradar.me/.
245 stars 17 forks source link

26484150: insertItemsAtIndexPaths throws NSInternalInconsistencyException when called right after UICollectionView view was created. #14745

Open openradar-mirror opened 8 years ago

openradar-mirror commented 8 years ago

Description

Summary:

To insert or delete some items the collection view must know how many items it had before and after. That means insert\delete operations will trigger data source methods to get those numbers. Typically, the collection view knows how many items it has by the moment we insert\delete. Because it invokes data source methods starting from the moment when we lay it out. So usually it will query the data source only once on insert\delete: to get the updated numbers after the items were inserted\removed.

But, if we created a collection view from scratch and immediately tried to insert\delete data, the collection view would trigger the data source methods twice! First time to get the numbers before the change, and second time to see how they have changed. That means there's no way to change the number of items returned by the data source methods in between of those two queries, which leads to NSInternalInconsistencyException.

Calling reloadData before the update doesn't invoke data source methods immediately, but rather invalidates some collection view's internal state and queries for the data only when necessary. That's quite obviously made for optimization purposes.

The only reliable way to force the collection view to read the current number of items before the insertion/deletion is either:

1) [collectionView performBatchUpdates:^{ // Invokes initial numbedOfItemsInSection if needed. // Now update the model here. [collectionView insertItemsAtIndexPaths:_indexpaths]; } completion:nil]; or 2) [collectionView numberOfItemsInSection:section]; // Force the initial data source call. [collectionView insertItemsAtIndexPaths:_indexpaths];

Steps to Reproduce: Compile and run the following code:

@interface ViewController : UIViewController @end

@implementation ViewController { NSUInteger _numberOfItems; }

@end

Expected Results: UICollectionView performs the insertion and doesn't throw NSInternalInconsistencyException. OR the official documentation describes this issue and provides some recommendations how to bypass it.

Actual Results: 2016-05-25 17:46:29.844 UICollectionView Tweaks[29712:7489886] * Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.60.7/UICollectionView.m:4625 2016-05-25 17:46:29.847 UICollectionView Tweaks[29712:7489886] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (1) must be equal to the number of items contained in that section before the update (1), plus or minus the number of items inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).' *\ First throw call stack: ( 0 CoreFoundation 0x0000000106e78d85 exceptionPreprocess + 165 1 libobjc.A.dylib 0x00000001068eadeb objc_exception_throw + 48 2 CoreFoundation 0x0000000106e78bea +[NSException raise:format:arguments:] + 106 3 Foundation 0x0000000106533d5a -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 198 4 UIKit 0x0000000107abe077 -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:] + 15363 5 UIKit 0x0000000107aba2ca -[UICollectionView _updateRowsAtIndexPaths:updateAction:] + 350 6 UICollectionView Tweaks 0x00000001063e53f9 -[ViewController insertItemsIntoCollectionView] + 217 7 UICollectionView Tweaks 0x00000001063e4efc -[ViewController viewDidLoad] + 92 8 UIKit 0x00000001073ca984 -[UIViewController loadViewIfRequired] + 1198 9 UIKit 0x00000001073cacd3 -[UIViewController view] + 27 10 UIKit 0x00000001072a0fb4 -[UIWindow addRootViewControllerViewIfPossible] + 61 11 UIKit 0x00000001072a169d -[UIWindow _setHidden:forced:] + 282 12 UIKit 0x00000001072b3180 -[UIWindow makeKeyAndVisible] + 42 13 UICollectionView Tweaks 0x00000001063e56e3 -[AppDelegate application:didFinishLaunchingWithOptions:] + 483 14 UIKit 0x00000001072269ac -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 272 15 UIKit 0x0000000107227c0d -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3415 16 UIKit 0x000000010722e568 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1769 17 UIKit 0x000000010722b714 -[UIApplication workspaceDidEndTransaction:] + 188 18 FrontBoardServices 0x0000000109cca8c8 FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK + 24 19 FrontBoardServices 0x0000000109cca741 -[FBSSerialQueue _performNext] + 178 20 FrontBoardServices 0x0000000109ccaaca -[FBSSerialQueue _performNextFromRunLoopSource] + 45 21 CoreFoundation 0x0000000106d9e301 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 17 22 CoreFoundation 0x0000000106d9422c CFRunLoopDoSources0 + 556 23 CoreFoundation 0x0000000106d936e3 __CFRunLoopRun + 867 24 CoreFoundation 0x0000000106d930f8 CFRunLoopRunSpecific + 488 25 UIKit 0x000000010722af21 -[UIApplication _run] + 402 26 UIKit 0x000000010722ff09 UIApplicationMain + 171 27 UICollectionView Tweaks 0x00000001063e54df main + 111 28 libdyld.dylib 0x000000010966a92d start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException

Product Version: 9.3.2 Created: 2016-05-26T01:19:10.176560 Originated: 2016-05-25T00:00:00 Open Radar Link: http://www.openradar.me/26484150

openradar-mirror commented 8 years ago

Modified: 2016-05-26T01:19:10.176750