-
-
Save iwasrobbed/5528897 to your computer and use it in GitHub Desktop.
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller | |
{ | |
self.shouldReloadCollectionView = NO; | |
self.blockOperation = [[NSBlockOperation alloc] init]; | |
} | |
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo | |
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type | |
{ | |
__weak UICollectionView *collectionView = self.collectionView; | |
switch (type) { | |
case NSFetchedResultsChangeInsert: { | |
[self.blockOperation addExecutionBlock:^{ | |
[collectionView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]]; | |
}]; | |
break; | |
} | |
case NSFetchedResultsChangeDelete: { | |
[self.blockOperation addExecutionBlock:^{ | |
[collectionView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]]; | |
}]; | |
break; | |
} | |
case NSFetchedResultsChangeUpdate: { | |
[self.blockOperation addExecutionBlock:^{ | |
[collectionView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex]]; | |
}]; | |
break; | |
} | |
default: | |
break; | |
} | |
} | |
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath | |
{ | |
__weak UICollectionView *collectionView = self.collectionView; | |
switch (type) { | |
case NSFetchedResultsChangeInsert: { | |
if ([self.collectionView numberOfSections] > 0) { | |
if ([self.collectionView numberOfItemsInSection:indexPath.section] == 0) { | |
self.shouldReloadCollectionView = YES; | |
} else { | |
[self.blockOperation addExecutionBlock:^{ | |
[collectionView insertItemsAtIndexPaths:@[newIndexPath]]; | |
}]; | |
} | |
} else { | |
self.shouldReloadCollectionView = YES; | |
} | |
break; | |
} | |
case NSFetchedResultsChangeDelete: { | |
if ([self.collectionView numberOfItemsInSection:indexPath.section] == 1) { | |
self.shouldReloadCollectionView = YES; | |
} else { | |
[self.blockOperation addExecutionBlock:^{ | |
[collectionView deleteItemsAtIndexPaths:@[indexPath]]; | |
}]; | |
} | |
break; | |
} | |
case NSFetchedResultsChangeUpdate: { | |
[self.blockOperation addExecutionBlock:^{ | |
[collectionView reloadItemsAtIndexPaths:@[indexPath]]; | |
}]; | |
break; | |
} | |
case NSFetchedResultsChangeMove: { | |
[self.blockOperation addExecutionBlock:^{ | |
[collectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; | |
}]; | |
break; | |
} | |
default: | |
break; | |
} | |
} | |
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller | |
{ | |
// Checks if we should reload the collection view to fix a bug @ http://openradar.appspot.com/12954582 | |
if (self.shouldReloadCollectionView) { | |
[self.collectionView reloadData]; | |
} else { | |
[self.collectionView performBatchUpdates:^{ | |
[self.blockOperation start]; | |
} completion:nil]; | |
} | |
} |
@MatejBalantic, according Apple Docs:
Operation objects are synchronous by default. In a synchronous operation, the operation object does not create a separate thread on which to run its task. When you call the start method of a synchronous operation directly from your code, the operation executes immediately in the current thread. By the time the start method of such an object returns control to the caller, the task itself is complete.
@pomozoff This is incorrect. When creating your own NSOperation subclass, it will operate synchronously if you call start, but an NSBlockOperation is different. From the NSBlockOperation Apple docs:
"The NSBlockOperation class is a concrete subclass of NSOperation that manages the concurrent execution of one or more blocks. You can use this object to execute several blocks at once without having to create separate operation objects for each......"
"Blocks added to a block operation are dispatched with default priority to an appropriate work queue. The blocks themselves should not make any assumptions about the configuration of their execution environment."
I understand that internally the block operation is dispatching the blocks to background threads, and indeed logging out the thread number from within the queued blocks yields:
2015-10-26 09:31:10.414 [76548:1833049] <NSThread: 0x7e922e80>{number = 37, name = (null)}
2015-10-26 09:31:10.414 [76548:1833062] <NSThread: 0x7bf81b80>{number = 33, name = (null)}
2015-10-26 09:31:10.414 [76548:1831231] <NSThread: 0x7d1380b0>{number = 1, name = main}
2015-10-26 09:31:10.414 [76548:1833061] <NSThread: 0x7be2efd0>{number = 35, name = (null)}
2015-10-26 09:31:10.414 [76548:1833063] <NSThread: 0x7e823f10>{number = 36, name = (null)}
2015-10-26 09:31:10.414 [76548:1833037] <NSThread: 0x7d2c0b30>{number = 34, name = (null)}
2015-10-26 09:31:10.414 [76548:1833064] <NSThread: 0x7d5273b0>{number = 32, name = (null)}
2015-10-26 09:31:10.414 [76548:1833050] <NSThread: 0x7d720e70>{number = 38, name = (null)}
OK. scrap that ...
@MatejBalantic you're correct about using [[NSOperationQueue currentQueue] addOperation:self.blockOperation];
v.s. [self.blockOperation start];
. If I use blockOperation.start
, NSFetchedResultsChangeUpdate
events aren't always processed properly (or at all).
Here's an updated controllerDidChangeContent
method that works for me:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// Checks if we should reload the collection view to fix a bug @ http://openradar.appspot.com/12954582
if (self.shouldReloadCollectionView) {
[self.collectionView reloadData];
} else {
[self.collectionView performBatchUpdates:^{
[[NSOperationQueue currentQueue] addOperation:self.blockOperation];
} completion:nil];
}
}
Thanks.
Hi guys, can anyone tell whats the status of the bug http://openradar.appspot.com/12954582 ?
Do we still have to use reloadData
on insertion of first element to collection views in iOS 9+ ?
I was getting errors from the thread sanitizer that blocks were running off the main thread. Changed to use an array of raw blocks and calling them directly rather than running them through an NSBlockOperation.
https://gist.github.com/fdstevex/7a782bb864b7b23b8d8a8e2393286fac
Hey guys, just mind that this is unsafe as it performs updates on the UICollectionViews on the non-main thread. Solution would be to schedule the execution of the blocks on the current thread: