Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ASCollectionNode] Add -isProcessingUpdates and -onDidFinishProcessingUpdates: APIs. #522

Merged
merged 6 commits into from
Aug 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## master

* Add your own contributions to the next release on the line below this with your name.
- [ASCollectionNode] Add -isProcessingUpdates and -onDidFinishProcessingUpdates: APIs. [#522](https://github.com/TextureGroup/Texture/pull/522) [Scott Goodson](https://github.com/appleguy)
- [Accessibility] Add .isAccessibilityContainer property, allowing automatic aggregation of children's a11y labels. [#468][Scott Goodson](https://github.com/appleguy)
- [ASImageNode] Enabled .clipsToBounds by default, fixing the use of .cornerRadius and clipping of GIFs. [Scott Goodson](https://github.com/appleguy) [#466](https://github.com/TextureGroup/Texture/pull/466)
- Fix an issue in layout transition that causes it to unexpectedly use the old layout [Huy Nguyen](https://github.com/nguyenhuy) [#464](https://github.com/TextureGroup/Texture/pull/464)
Expand Down
37 changes: 34 additions & 3 deletions Source/ASCollectionNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,39 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;

/**
* Returns YES if the ASCollectionNode is still processing changes from performBatchUpdates:.
* This is typically the concurrent allocation (calling nodeBlocks) and layout of newly inserted
* ASCellNodes. If YES is returned, then calling -waitUntilAllUpdatesAreProcessed may take tens of
* milliseconds to return as it blocks on these concurrent operations.
*
* Returns NO if ASCollectionNode is fully synchronized with the underlying UICollectionView. This
* means that until the next performBatchUpdates: is called, it is safe to compare UIKit values
* (such as from UICollectionViewLayout) with your app's data source.
*
* This method will always return NO if called immediately after -waitUntilAllUpdatesAreProcessed.
*/
@property (nonatomic, readonly) BOOL isProcessingUpdates;

/**
* Schedules a block to be performed (on the main thread) after processing of performBatchUpdates:
* is finished (completely synchronized to UIKit). The blocks will be run at the moment that
* -isProcessingUpdates changes from YES to NO;
*
* When isProcessingUpdates == NO, the block is run block immediately (before the method returns).
*
* Blocks scheduled by this mechanism are NOT guaranteed to run in the order they are scheduled.
* They may also be delayed if performBatchUpdates continues to be called; the blocks will wait until
* all running updates are finished.
*
* Calling -waitUntilAllUpdatesAreProcessed is one way to flush any pending update completion blocks.
*/
- (void)onDidFinishProcessingUpdates:(nullable void (^)())didFinishProcessingUpdates;

/**
* Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread.
*/
- (void)waitUntilAllUpdatesAreCommitted;
- (void)waitUntilAllUpdatesAreProcessed;

/**
* Inserts one or more sections.
Expand Down Expand Up @@ -501,9 +530,11 @@ NS_ASSUME_NONNULL_BEGIN
* @warning This method is substantially more expensive than UICollectionView's version.
*
* @deprecated This method is deprecated in 2.0. Use @c reloadDataWithCompletion: and
* then @c waitUntilAllUpdatesAreCommitted instead.
* then @c waitUntilAllUpdatesAreProcessed instead.
*/
- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use -reloadData / -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreCommitted instead.");
- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use -reloadData / -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreProcessed instead.");

- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("This method has been renamed to -waitUntilAllUpdatesAreProcessed.");

@end

Expand Down
23 changes: 21 additions & 2 deletions Source/ASCollectionNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -705,14 +705,33 @@ - (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))compl
[self performBatchAnimated:UIView.areAnimationsEnabled updates:updates completion:completion];
}

- (void)waitUntilAllUpdatesAreCommitted
- (BOOL)isProcessingUpdates
{
return (self.nodeLoaded ? [self.view isProcessingUpdates] : NO);
}

- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
if (!self.nodeLoaded) {
completion();
} else {
[self.view onDidFinishProcessingUpdates:completion];
}
}

- (void)waitUntilAllUpdatesAreProcessed
{
ASDisplayNodeAssertMainThread();
if (self.nodeLoaded) {
[self.view waitUntilAllUpdatesAreCommitted];
}
}

- (void)waitUntilAllUpdatesAreCommitted
{
[self waitUntilAllUpdatesAreProcessed];
}

- (void)reloadDataWithCompletion:(void (^)())completion
{
ASDisplayNodeAssertMainThread();
Expand All @@ -738,7 +757,7 @@ - (void)reloadDataImmediately
{
ASDisplayNodeAssertMainThread();
[self reloadData];
[self waitUntilAllUpdatesAreCommitted];
[self waitUntilAllUpdatesAreProcessed];
}

- (void)relayoutItems
Expand Down
6 changes: 4 additions & 2 deletions Source/ASCollectionView.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,11 @@ NS_ASSUME_NONNULL_BEGIN
- (void)relayoutItems ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");

/**
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
* See ASCollectionNode.h for full documentation of these methods.
*/
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");
@property (nonatomic, readonly) BOOL isProcessingUpdates;
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion;
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use -[ASCollectionNode waitUntilAllUpdatesAreProcessed] instead.");

/**
* Registers the given kind of supplementary node for use in creating node-backed supplementary views.
Expand Down
16 changes: 13 additions & 3 deletions Source/ASCollectionView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,16 @@ - (void)relayoutItems
[_dataController relayoutAllNodes];
}

- (BOOL)isProcessingUpdates
{
return [_dataController isProcessingUpdates];
}

- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
[_dataController onDidFinishProcessingUpdates:completion];
}

- (void)waitUntilAllUpdatesAreCommitted
{
ASDisplayNodeAssertMainThread();
Expand All @@ -367,8 +377,8 @@ - (void)waitUntilAllUpdatesAreCommitted
// ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd));
return;
}
[_dataController waitUntilAllUpdatesAreCommitted];

[_dataController waitUntilAllUpdatesAreProcessed];
}

- (void)setDataSource:(id<UICollectionViewDataSource>)dataSource
Expand Down Expand Up @@ -2197,7 +2207,7 @@ - (void)layer:(CALayer *)layer didChangeBoundsWithOldValue:(CGRect)oldBounds new

if (changedInNonScrollingDirection) {
[_dataController relayoutAllNodes];
[_dataController waitUntilAllUpdatesAreCommitted];
[_dataController waitUntilAllUpdatesAreProcessed];
// We need to ensure the size requery is done before we update our layout.
[self.collectionViewLayout invalidateLayout];
}
Expand Down
39 changes: 37 additions & 2 deletions Source/ASTableNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,38 @@ NS_ASSUME_NONNULL_BEGIN
- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;

/**
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
* Returns YES if the ASCollectionNode is still processing changes from performBatchUpdates:.
* This is typically the concurrent allocation (calling nodeBlocks) and layout of newly inserted
* ASCellNodes. If YES is returned, then calling -waitUntilAllUpdatesAreProcessed may take tens of
* milliseconds to return as it blocks on these concurrent operations.
*
* Returns NO if ASCollectionNode is fully synchronized with the underlying UICollectionView. This
* means that until the next performBatchUpdates: is called, it is safe to compare UIKit values
* (such as from UICollectionViewLayout) with your app's data source.
*
* This method will always return NO if called immediately after -waitUntilAllUpdatesAreProcessed.
*/
@property (nonatomic, readonly) BOOL isProcessingUpdates;

/**
* Schedules a block to be performed (on the main thread) after processing of performBatchUpdates:
* is finished (completely synchronized to UIKit). The blocks will be run at the moment that
* -isProcessingUpdates changes from YES to NO;
*
* When isProcessingUpdates == NO, the block is run block immediately (before the method returns).
*
* Blocks scheduled by this mechanism are NOT guaranteed to run in the order they are scheduled.
* They may also be delayed if performBatchUpdates continues to be called; the blocks will wait until
* all running updates are finished.
*
* Calling -waitUntilAllUpdatesAreProcessed is one way to flush any pending update completion blocks.
*/
- (void)waitUntilAllUpdatesAreCommitted;
- (void)onDidFinishProcessingUpdates:(nullable void (^)())didFinishProcessingUpdates;

/**
* Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread.
*/
- (void)waitUntilAllUpdatesAreProcessed;

/**
* Inserts one or more sections, with an option to animate the insertion.
Expand Down Expand Up @@ -699,4 +728,10 @@ NS_ASSUME_NONNULL_BEGIN

@end

@interface ASTableNode (Deprecated)

- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("This method has been renamed to -waitUntilAllUpdatesAreProcessed.");

@end

NS_ASSUME_NONNULL_END
21 changes: 20 additions & 1 deletion Source/ASTableNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -733,14 +733,33 @@ - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)n
}
}

- (void)waitUntilAllUpdatesAreCommitted
- (BOOL)isProcessingUpdates
{
return (self.nodeLoaded ? [self.view isProcessingUpdates] : NO);
}

- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
if (!self.nodeLoaded) {
completion();
} else {
[self.view onDidFinishProcessingUpdates:completion];
}
}

- (void)waitUntilAllUpdatesAreProcessed
{
ASDisplayNodeAssertMainThread();
if (self.nodeLoaded) {
[self.view waitUntilAllUpdatesAreCommitted];
}
}

- (void)waitUntilAllUpdatesAreCommitted
{
[self waitUntilAllUpdatesAreProcessed];
}

#pragma mark - Debugging (Private)

- (NSMutableArray<NSDictionary *> *)propertiesForDebugDescription
Expand Down
6 changes: 4 additions & 2 deletions Source/ASTableView.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,11 @@ NS_ASSUME_NONNULL_BEGIN
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's -performBatchUpdates:completion: instead.");

/**
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
* See ASTableNode.h for full documentation of these methods.
*/
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");
@property (nonatomic, readonly) BOOL isProcessingUpdates;
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion;
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use -[ASTableNode waitUntilAllUpdatesAreProcessed] instead.");

- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead.");

Expand Down
16 changes: 13 additions & 3 deletions Source/ASTableView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ - (void)reloadDataImmediately
{
ASDisplayNodeAssertMainThread();
[self reloadData];
[_dataController waitUntilAllUpdatesAreCommitted];
[_dataController waitUntilAllUpdatesAreProcessed];
}

- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
Expand Down Expand Up @@ -735,6 +735,16 @@ - (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))c
}
}

- (BOOL)isProcessingUpdates
{
return [_dataController isProcessingUpdates];
}

- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
[_dataController onDidFinishProcessingUpdates:completion];
}

- (void)waitUntilAllUpdatesAreCommitted
{
ASDisplayNodeAssertMainThread();
Expand All @@ -743,8 +753,8 @@ - (void)waitUntilAllUpdatesAreCommitted
// ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd));
return;
}
[_dataController waitUntilAllUpdatesAreCommitted];

[_dataController waitUntilAllUpdatesAreProcessed];
}

- (void)layoutSubviews
Expand Down
7 changes: 6 additions & 1 deletion Source/Details/ASDataController.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,12 @@ extern NSString * const ASCollectionInvalidUpdateException;
*/
- (void)relayoutNodes:(id<NSFastEnumeration>)nodes nodesSizeChanged:(NSMutableArray * _Nonnull)nodesSizesChanged;

- (void)waitUntilAllUpdatesAreCommitted;
/**
* See ASCollectionNode.h for full documentation of these methods.
*/
@property (nonatomic, readonly) BOOL isProcessingUpdates;
- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion;
- (void)waitUntilAllUpdatesAreProcessed;

/**
* Notifies the data controller object that its environment has changed. The object will request its environment delegate for new information
Expand Down
33 changes: 31 additions & 2 deletions Source/Details/ASDataController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,42 @@ - (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSInde

#pragma mark - Batching (External API)

- (void)waitUntilAllUpdatesAreCommitted
- (void)waitUntilAllUpdatesAreProcessed
{
// Schedule block in main serial queue to wait until all operations are finished that are
// where scheduled while waiting for the _editingTransactionQueue to finish
[self _scheduleBlockOnMainSerialQueue:^{ }];
}

- (BOOL)isProcessingUpdates
{
ASDisplayNodeAssertMainThread();
if (_mainSerialQueue.numberOfScheduledBlocks > 0) {
return YES;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

} else if (dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_NOW) != 0) {
// After waiting for zero duration, a nonzero value is returned if blocks are still running.
return YES;
}
// Both the _mainSerialQueue and _editingTransactionQueue are drained; we are fully quiesced.
return NO;
}

- (void)onDidFinishProcessingUpdates:(nullable void (^)())completion
{
ASDisplayNodeAssertMainThread();
if ([self isProcessingUpdates] == NO) {
ASPerformBlockOnMainThread(completion);
} else {
dispatch_async(_editingTransactionQueue, ^{
// Retry the block. If we're done processing updates, it'll run immediately, otherwise
// wait again for updates to quiesce completely.
[_mainSerialQueue performBlockOnMainThread:^{
[self onDidFinishProcessingUpdates:completion];
}];
});
}
}

- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet
{
ASDisplayNodeAssertMainThread();
Expand Down Expand Up @@ -563,7 +592,7 @@ - (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet
});

if (_usesSynchronousDataLoading) {
[self waitUntilAllUpdatesAreCommitted];
[self waitUntilAllUpdatesAreProcessed];
}
}

Expand Down
1 change: 1 addition & 0 deletions Source/Details/ASMainSerialQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
AS_SUBCLASSING_RESTRICTED
@interface ASMainSerialQueue : NSObject

@property (nonatomic, readonly) NSUInteger numberOfScheduledBlocks;
- (void)performBlockOnMainThread:(dispatch_block_t)block;

@end
6 changes: 6 additions & 0 deletions Source/Details/ASMainSerialQueue.mm
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ - (instancetype)init
return self;
}

- (NSUInteger)numberOfScheduledBlocks
{
ASDN::MutexLocker l(_serialQueueLock);
return _blocks.count;
}

- (void)performBlockOnMainThread:(dispatch_block_t)block
{
ASDN::MutexLocker l(_serialQueueLock);
Expand Down
2 changes: 1 addition & 1 deletion Tests/ASCollectionModernDataSourceTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ - (void)setUp {

- (void)tearDown
{
[collectionNode waitUntilAllUpdatesAreCommitted];
[collectionNode waitUntilAllUpdatesAreProcessed];
[super tearDown];
}

Expand Down
Loading