diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 575f22bb71..a47765bbfa 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -79,9 +79,8 @@ @property (nonatomic, assign) CGFloat leadingScreensForBatching; /** - * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. You can call it from background - * thread (it is recommendated) and the UI collection view will be updated asynchronously. The asyncDataSource must be updated - * to reflect the changes before this method is called. + * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. + * The asyncDataSource must be updated to reflect the changes before the update block completes. * * @param animated NO to disable animations for this batch * @param updates The block that performs the relevant insert, delete, reload, or move operations. @@ -92,8 +91,8 @@ - (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion; /** - * Perform a batch of updates asynchronously. You can call it from background thread (it is recommendated) and the UI collection - * view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes before this method is called. + * Perform a batch of updates asynchronously. This method must be called from the main thread. + * The asyncDataSource must be updated to reflect the changes before update block completes. * * @param updates The block that performs the relevant insert, delete, reload, or move operations. * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single @@ -123,8 +122,7 @@ * * @param sections An index set that specifies the sections to insert. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)insertSections:(NSIndexSet *)sections; @@ -134,8 +132,7 @@ * * @param sections An index set that specifies the sections to delete. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)deleteSections:(NSIndexSet *)sections; @@ -145,8 +142,7 @@ * * @param sections An index set that specifies the sections to reload. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)reloadSections:(NSIndexSet *)sections; @@ -158,8 +154,7 @@ * * @param newSection The index that is the destination of the move for the section. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; @@ -169,8 +164,7 @@ * * @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths; @@ -180,8 +174,7 @@ * * @param indexPaths An array of NSIndexPath objects identifying the items to delete. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths; @@ -191,8 +184,7 @@ * * @param indexPaths An array of NSIndexPath objects identifying the items to reload. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths; @@ -204,8 +196,7 @@ * * @param newIndexPath The index path that is the destination of the move for the item. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 29260aa86a..6244bdd27b 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -283,6 +283,8 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion { + ASDisplayNodeAssertMainThread(); + [_dataController beginUpdates]; updates(); [_dataController endUpdatesAnimated:animated completion:completion]; @@ -295,41 +297,49 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)insertSections:(NSIndexSet *)sections { + ASDisplayNodeAssertMainThread(); [_dataController insertSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)deleteSections:(NSIndexSet *)sections { + ASDisplayNodeAssertMainThread(); [_dataController deleteSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)reloadSections:(NSIndexSet *)sections { + ASDisplayNodeAssertMainThread(); [_dataController reloadSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection { + ASDisplayNodeAssertMainThread(); [_dataController moveSection:section toSection:newSection withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths { + ASDisplayNodeAssertMainThread(); [_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths { + ASDisplayNodeAssertMainThread(); [_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths { + ASDisplayNodeAssertMainThread(); [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath { + ASDisplayNodeAssertMainThread(); [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:kASCollectionViewAnimationNone]; } diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 816a41f0a9..c8dd0e2b81 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -94,7 +94,7 @@ - (void)reloadData; /** - * begins a batch of insert, delete reload and move operations. Batches are asynchronous an thread safe. + * begins a batch of insert, delete reload and move operations. This method must be called from the main thread. */ - (void)beginUpdates; @@ -102,7 +102,7 @@ * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. * You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations * to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating - * the operations simultaneously. This method is asynchronous and thread safe. It's important to remeber that the ASTableView will + * the operations simultaneously. This method is must be called from the main thread. It's important to remeber that the ASTableView will * be processing the updates asynchronously after this call is completed. * * @param animated NO to disable all animations. @@ -116,7 +116,7 @@ * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. * You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations * to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating - * the operations simultaneously. This method is asynchronous and thread safe. It's important to remeber that the ASTableView will + * the operations simultaneously. This method is must be called from the main thread. It's important to remeber that the ASTableView will * be processing the updates asynchronously after this call and are not guaranteed to be reflected in the ASTableView until * the completion block is executed. * @@ -134,8 +134,7 @@ * * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; @@ -147,8 +146,7 @@ * * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; @@ -160,8 +158,7 @@ * * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; @@ -173,8 +170,7 @@ * * @param newSection The index that is the destination of the move for the section. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; @@ -186,8 +182,7 @@ * * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; @@ -199,8 +194,7 @@ * * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; @@ -212,8 +206,7 @@ * * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; @@ -225,8 +218,7 @@ * * @param newIndexPath The index path that is the destination of the move for the row. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; @@ -249,7 +241,8 @@ /** * YES to automatically adjust the contentOffset when cells are inserted or deleted "before" - * visible cells, maintaining the users' visible scroll position. + * visible cells, maintaining the users' visible scroll position. Currently this feature tracks insertions, moves and deletions of + * cells, but section edits are ignored. * * default is NO. */ diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 9645f30e9e..6aa33f5141 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -328,6 +328,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)beginUpdates { + ASDisplayNodeAssertMainThread(); [_dataController beginUpdates]; } @@ -338,6 +339,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion; { + ASDisplayNodeAssertMainThread(); [_dataController endUpdatesAnimated:animated completion:completion]; } @@ -346,41 +348,49 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController insertSections:sections withAnimationOptions:animation]; } - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController deleteSections:sections withAnimationOptions:animation]; } - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController reloadSections:sections withAnimationOptions:animation]; } - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection { + ASDisplayNodeAssertMainThread(); [_dataController moveSection:section toSection:newSection withAnimationOptions:UITableViewRowAnimationNone]; } - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath { + ASDisplayNodeAssertMainThread(); [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:UITableViewRowAnimationNone]; } @@ -416,7 +426,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { CGFloat adjustment = 0; NSIndexPath *top = _contentOffsetAdjustmentTopVisibleRow ?: self.indexPathsForVisibleRows.firstObject; - for (int index=0; index