nek023 / QBImagePicker

A clone of UIImagePickerController with multiple selection support.
MIT License
1.78k stars 552 forks source link

Crash in - [QBAssetsViewController photoLibraryDidChange:] in QBAssetsViewController.m, line 403 #88

Open knutigro opened 9 years ago

knutigro commented 9 years ago

Info: *\ Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete and reload the same index path (<NSIndexPath: 0x18a62f00> {length = 2, path = 0 - 94})'

Exception Type: SIGABRT

Hardware Model: iPad3,4 OS Version: iPhone OS 8.4 (12H143)

Exception Type: SIGABRT Exception Codes: #0 at 0x316addf0 Crashed Thread: 0

Application Specific Information: *\ Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete and reload the same index path (<NSIndexPath: 0x18a62f00> {length = 2, path = 0 - 94})'

Last Exception Backtrace: 0 CoreFoundation 0x22973fef exceptionPreprocess + 127 1 libobjc.A.dylib 0x31031c8b objc_exception_throw + 36 2 CoreFoundation 0x22973ec5 +[NSException raise:format:arguments:] + 102 3 Foundation 0x23673f17 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 88 4 UIKit 0x265f0711 -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:] + 1342 5 UIKit 0x265f492f -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:] + 404 6 UIKit 0x265f4793 -[UICollectionView _performBatchUpdates:completion:invalidationContext:] + 28 7 UIKit 0x261b1617 -[UICollectionView performBatchUpdates:completion:] + 28 8 Foap 0x002b6b01 __48-[QBAssetsViewController photoLibraryDidChange:]_block_invoke (QBAssetsViewController.m:403) 9 libdispatch.dylib 0x315c22e3 _dispatch_call_block_and_release + 8 10 libdispatch.dylib 0x315c22cf _dispatch_client_callout + 20 11 libdispatch.dylib 0x315c5d2f _dispatch_main_queue_callback_4CF + 1328 12 CoreFoundation 0x22939609 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 6 13 CoreFoundation 0x22937d09 __CFRunLoopRun + 1510 14 CoreFoundation 0x22884201 CFRunLoopRunSpecific + 474 15 CoreFoundation 0x22884013 CFRunLoopRunInMode + 104 16 GraphicsServices 0x2a243201 GSEventRunModal + 134 17 UIKit 0x26050a09 UIApplicationMain + 1438 18 Foap 0x0003b2ab main (main.m:15) 19 libdyld.dylib 0x315e3aaf start + 0

burczyk commented 8 years ago

Hello, I faced similar problem with one of my apps. QBAssetsViewController.m line 398 __48-[QBAssetsViewController photoLibraryDidChange:]_block_invoke

Fatal Exception: NSInternalInconsistencyException attempt to delete item 57 from section 0 which only contains 57 items before the update

Thread : Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x1839fcf48 __exceptionPreprocess
1  libobjc.A.dylib                0x1985aff80 objc_exception_throw
2  CoreFoundation                 0x1839fce18 +[NSException raise:format:]
3  Foundation                     0x1848f0a1c -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4  UIKit                          0x1897af2e4 -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:]
5  UIKit                          0x1897b45c8 -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:]
6  UIKit                          0x1897b4444 -[UICollectionView _performBatchUpdates:completion:invalidationContext:]
7  UIKit                          0x18917e99c -[UICollectionView performBatchUpdates:completion:]
8  QBImagePickerController        0x1011c32a4 __48-[QBAssetsViewController photoLibraryDidChange:]_block_invoke (QBAssetsViewController.m:398)
9  libdispatch.dylib              0x198dcd6e8 _dispatch_call_block_and_release
10 libdispatch.dylib              0x198dcd6a8 _dispatch_client_callout
11 libdispatch.dylib              0x198dd2db0 _dispatch_main_queue_callback_4CF
12 CoreFoundation                 0x1839b41f8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
13 CoreFoundation                 0x1839b2060 __CFRunLoopRun
14 CoreFoundation                 0x1838e0ca0 CFRunLoopRunSpecific
15 GraphicsServices               0x18eb1c088 GSEventRunModal
16 UIKit                          0x188ff8ffc UIApplicationMain
17 ...                            0x1000d44b4 main (AppDelegate.swift:21)
18 libdyld.dylib                  0x198dfe8b8 start

Looks like indexes calculation may be wrong somehow while updating. Do you have any assumptions why it crashes?

knutigro commented 8 years ago

@burczyk actually I didn't really try to find a sollution to this crash yet but I probably should since its one of the biggest crash groups in my app for the moment. There is a thread here: http://stackoverflow.com/questions/29337765/crash-attempt-to-delete-and-reload-the-same-index-path which suggest it has something to do with iCloud sync. It also provides a sollution. I did try to reproduce the crash, like mentioned in this thread, but had no success... Maybe its time for a new try ...

burczyk commented 8 years ago

I also didn't reproduce it yet. I only saw it in Crashlytics, 3 times. It probably doesn't matter but each crash occurred on iPhone 6 Plus running iOS 9.1.0 (although for different users, so it may be a coincidence).

knutigro commented 8 years ago

I have been able to reproduce this today using following steps (taken from the accepted answer in this thread: http://stackoverflow.com/questions/29337765/crash-attempt-to-delete-and-reload-the-same-index-path):

  1. Open your app that is listening for changes
  2. Open the photos app, save a set of photos to your photo library from an iCloud shared album
  3. Go to the photos app, delete some of those photos
  4. Go again to the iCloud shared album and save again the some of the photos you deleted. You'll see this condition happen.

It happens when the same index exist in both removedIndexes and changedIndexes. I will create a pullrequest with a suggsted sollution.

knutigro commented 8 years ago

It seemes like my original idea how to prevent this crash did not work :-( The search goes on...

Yaou commented 8 years ago

same problem with my app, any news?

ingscjoshua commented 8 years ago

Hello i'm have the same problem

yao23 commented 7 years ago

same issue, any solution?

ryderjack commented 7 years ago

bump

abdullahumer commented 5 years ago

Has this library been abandoned? I bumped the same crash in the latest version

Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x184a5ad8c __exceptionPreprocess
1  libobjc.A.dylib                0x183c145ec objc_exception_throw
2  CoreFoundation                 0x184a5abf8 +[NSException raise:format:]
3  Foundation                     0x18544afa0 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4  UIKit                          0x18e7b2174 -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:]
5  UIKit                          0x18e7b1abc -[UICollectionView _endUpdatesWithInvalidationContext:tentativelyForReordering:animator:]
6  UIKit                          0x18e7b0c48 -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:animator:]
7  UIKit                          0x18e7b0aa4 -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:]
8  UIKit                          0x18e7b0a28 -[UICollectionView _performBatchUpdates:completion:invalidationContext:]
9  UIKit                          0x18e7b09c0 -[UICollectionView performBatchUpdates:completion:]
10 QBImagePickerController        0x10663f534 __48-[QBAssetsViewController photoLibraryDidChange:]_block_invoke (QBAssetsViewController.m:397)
11 libdispatch.dylib              0x18434caa0 _dispatch_call_block_and_release
12 libdispatch.dylib              0x18434ca60 _dispatch_client_callout
13 libdispatch.dylib              0x18438dd80 _dispatch_main_queue_callback_4CF$VARIANT$armv81
14 CoreFoundation                 0x184a03070 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
15 CoreFoundation                 0x184a00bc8 __CFRunLoopRun
16 CoreFoundation                 0x184920da8 CFRunLoopRunSpecific
17 GraphicsServices               0x186906020 GSEventRunModal
18 UIKit                          0x18e940758 UIApplicationMain
19 Haraj                          0x104ec2a1c main (main.m:16)
20 libdyld.dylib                  0x1843b1fc0 start
abdullahumer commented 5 years ago

Strange! 4 years and this issue still hasn't been solved. It's hard to reproduce. As someone said above, I also see it only in Crashlytics.

abdullahumer commented 5 years ago

I added code to catch the exception and reload just reload the collectionView data in case if an exception occurs. This is not exactly a fix but I hope the crash is prevented.

                @try {
                    [self.collectionView performBatchUpdates:^{
                        NSIndexSet *removedIndexes = [collectionChanges removedIndexes];
                        if ([removedIndexes count]) {
                            [self.collectionView deleteItemsAtIndexPaths:[removedIndexes qb_indexPathsFromIndexesWithSection:0]];
                        }

                        NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
                        if ([insertedIndexes count]) {
                            [self.collectionView insertItemsAtIndexPaths:[insertedIndexes qb_indexPathsFromIndexesWithSection:0]];
                        }

                        NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
                        if ([changedIndexes count]) {
                            [self.collectionView reloadItemsAtIndexPaths:[changedIndexes qb_indexPathsFromIndexesWithSection:0]];
                        }
                    } completion:NULL];
                } @catch (NSException *exception) {
                    [self.collectionView reloadData];
                } @finally {
                    //
                } 
Benny-iPhone commented 4 years ago

this is how I fixed the code, the idea is to find if removeIndexes and changedIndexes has any union.

- (void)photoLibraryDidChange:(PHChange *)changeInstance {
    __weak typeof(self) weakSelf = self;

    dispatch_async(dispatch_get_main_queue(), ^{
        typeof(self) self = weakSelf;

        if (!self) return;

        // If we have incremental diffs, tell the collection view to animate insertions and deletions
        void (^batchUpdated)(PHFetchResultChangeDetails *) = ^void(PHFetchResultChangeDetails *collectionChanges){
            [self.collectionView performBatchUpdates:^{
                NSIndexSet *removedIndexes = [collectionChanges removedIndexes];
                if ([removedIndexes count]) {
                    [self.collectionView deleteItemsAtIndexPaths:[removedIndexes qb_indexPathsFromIndexesWithSection:0]];
                }

                NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
                if ([insertedIndexes count]) {
                    [self.collectionView insertItemsAtIndexPaths:[insertedIndexes qb_indexPathsFromIndexesWithSection:0]];
                }

                NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
                if ([changedIndexes count]) {
                    [self.collectionView reloadItemsAtIndexPaths:[changedIndexes qb_indexPathsFromIndexesWithSection:0]];
                }
            } completion:NULL];
        };

        BOOL (^shouldReload)(PHFetchResultChangeDetails *) = ^BOOL(PHFetchResultChangeDetails *collectionChanges){
            if ([collectionChanges hasMoves]) { return YES; }
            if ([collectionChanges hasIncrementalChanges] == NO) { return YES; }
            if ([collectionChanges.removedIndexes containsAnyIndexOf:collectionChanges.changedIndexes]) {
                return YES;
            }

            return NO;
        };

        PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.fetchResult];
        if (collectionChanges == nil) { return; }

        self.fetchResult = [collectionChanges fetchResultAfterChanges];

        if (shouldReload(collectionChanges)){
            // We need to reload all if the incremental diffs are not available
            [self.collectionView reloadData];
        } else {
            batchUpdated(collectionChanges);
        }

        [self resetCachedAssets];
    });

}

Do not forget to add the next method to NSIndexSet category implementation

- (BOOL) containsAnyIndexOf:(NSIndexSet *)otherSet{
    __block BOOL result = NO;
    [otherSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
        if ([self containsIndex:idx]){
            result = YES;
            *stop = YES;
        }
    }];

    return result;
}