[ASAsyncTransaction] Make System Less Specific to Layer Display, More Readable (#2332)

* Make ASAsyncTransaction system less specific to layer-display, more readable

* Document weird import
This commit is contained in:
Adlai Holler 2016-10-04 20:07:22 -04:00 committed by GitHub
parent 2de129008f
commit 4dfe2a5a66
9 changed files with 134 additions and 85 deletions

View File

@ -856,7 +856,7 @@ extern NSInteger const ASDefaultDrawingPriority;
* ASDisplayNode participates in ASAsyncTransactions, so you can determine when your subnodes are done rendering. * ASDisplayNode participates in ASAsyncTransactions, so you can determine when your subnodes are done rendering.
* See: -(void)asyncdisplaykit_asyncTransactionContainerStateDidChange in ASDisplayNodeSubclass.h * See: -(void)asyncdisplaykit_asyncTransactionContainerStateDidChange in ASDisplayNodeSubclass.h
*/ */
@interface ASDisplayNode (ASDisplayNodeAsyncTransactionContainer) <ASDisplayNodeAsyncTransactionContainer> @interface ASDisplayNode (ASAsyncTransactionContainer) <ASAsyncTransactionContainer>
@end @end
/** UIVIew(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to an UIView. */ /** UIVIew(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to an UIView. */

View File

@ -10,14 +10,16 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#define ASDISPLAYNODE_DELAY_DISPLAY 0 #define ASDISPLAYNODE_DELAY_DISPLAY 0
@class _ASAsyncTransaction; @class _ASAsyncTransaction;
typedef void(^asyncdisplaykit_async_transaction_completion_block_t)(_ASAsyncTransaction *completedTransaction, BOOL canceled); typedef void(^asyncdisplaykit_async_transaction_completion_block_t)(_ASAsyncTransaction *completedTransaction, BOOL canceled);
typedef id<NSObject>(^asyncdisplaykit_async_transaction_operation_block_t)(void); typedef id<NSObject> _Nullable(^asyncdisplaykit_async_transaction_operation_block_t)(void);
typedef void(^asyncdisplaykit_async_transaction_operation_completion_block_t)(id<NSObject> value, BOOL canceled); typedef void(^asyncdisplaykit_async_transaction_operation_completion_block_t)(id<NSObject> _Nullable value, BOOL canceled);
typedef void(^asyncdisplaykit_async_transaction_complete_async_operation_block_t)(id<NSObject> value); typedef void(^asyncdisplaykit_async_transaction_complete_async_operation_block_t)(id<NSObject> _Nullable value);
typedef void(^asyncdisplaykit_async_transaction_async_operation_block_t)(asyncdisplaykit_async_transaction_complete_async_operation_block_t completeOperationBlock); typedef void(^asyncdisplaykit_async_transaction_async_operation_block_t)(asyncdisplaykit_async_transaction_complete_async_operation_block_t completeOperationBlock);
/** /**
@ -54,11 +56,11 @@ extern NSInteger const ASDefaultTransactionPriority;
@summary Initialize a transaction that can start collecting async operations. @summary Initialize a transaction that can start collecting async operations.
@see initWithCallbackQueue:commitBlock:completionBlock:executeConcurrently: @see initWithCallbackQueue:commitBlock:completionBlock:executeConcurrently:
@param callbackQueue The dispatch queue that the completion blocks will be called on. @param callbackQueue The dispatch queue that the completion blocks will be called on. Default is the main queue.
@param completionBlock A block that is called when the transaction is completed. May be NULL. @param completionBlock A block that is called when the transaction is completed.
*/ */
- (instancetype)initWithCallbackQueue:(dispatch_queue_t)callbackQueue - (instancetype)initWithCallbackQueue:(nullable dispatch_queue_t)callbackQueue
completionBlock:(asyncdisplaykit_async_transaction_completion_block_t)completionBlock; completionBlock:(nullable asyncdisplaykit_async_transaction_completion_block_t)completionBlock;
/** /**
@summary Block the main thread until the transaction is complete, including callbacks. @summary Block the main thread until the transaction is complete, including callbacks.
@ -70,18 +72,18 @@ extern NSInteger const ASDefaultTransactionPriority;
/** /**
The dispatch queue that the completion blocks will be called on. The dispatch queue that the completion blocks will be called on.
*/ */
@property (nonatomic, readonly, retain) dispatch_queue_t callbackQueue; @property (nonatomic, readonly, strong) dispatch_queue_t callbackQueue;
/** /**
A block that is called when the transaction is completed. A block that is called when the transaction is completed.
*/ */
@property (nonatomic, readonly, copy) asyncdisplaykit_async_transaction_completion_block_t completionBlock; @property (nonatomic, readonly, copy, nullable) asyncdisplaykit_async_transaction_completion_block_t completionBlock;
/** /**
The state of the transaction. The state of the transaction.
@see ASAsyncTransactionState @see ASAsyncTransactionState
*/ */
@property (nonatomic, readonly, assign) ASAsyncTransactionState state; @property (readonly, assign) ASAsyncTransactionState state;
/** /**
@summary Adds a synchronous operation to the transaction. The execution block will be executed immediately. @summary Adds a synchronous operation to the transaction. The execution block will be executed immediately.
@ -97,7 +99,7 @@ extern NSInteger const ASDefaultTransactionPriority;
*/ */
- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block - (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block
queue:(dispatch_queue_t)queue queue:(dispatch_queue_t)queue
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion; completion:(nullable asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
/** /**
@summary Adds a synchronous operation to the transaction. The execution block will be executed immediately. @summary Adds a synchronous operation to the transaction. The execution block will be executed immediately.
@ -115,7 +117,7 @@ extern NSInteger const ASDefaultTransactionPriority;
- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block - (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block
priority:(NSInteger)priority priority:(NSInteger)priority
queue:(dispatch_queue_t)queue queue:(dispatch_queue_t)queue
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion; completion:(nullable asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
/** /**
@ -134,7 +136,7 @@ extern NSInteger const ASDefaultTransactionPriority;
*/ */
- (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block - (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block
queue:(dispatch_queue_t)queue queue:(dispatch_queue_t)queue
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion; completion:(nullable asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
/** /**
@summary Adds an async operation to the transaction. The execution block will be executed immediately. @summary Adds an async operation to the transaction. The execution block will be executed immediately.
@ -154,7 +156,7 @@ extern NSInteger const ASDefaultTransactionPriority;
- (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block - (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block
priority:(NSInteger)priority priority:(NSInteger)priority
queue:(dispatch_queue_t)queue queue:(dispatch_queue_t)queue
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion; completion:(nullable asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
@ -189,3 +191,5 @@ extern NSInteger const ASDefaultTransactionPriority;
- (void)commit; - (void)commit;
@end @end
NS_ASSUME_NONNULL_END

View File

@ -8,24 +8,28 @@
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
// //
// We need this import for UITrackingRunLoopMode
#import <UIKit/UIApplication.h>
#import "_ASAsyncTransaction.h" #import "_ASAsyncTransaction.h"
#import "_ASAsyncTransactionGroup.h" #import "_ASAsyncTransactionGroup.h"
#import <list> #import <list>
#import <map> #import <map>
#import <mutex> #import <mutex>
#import <stdatomic.h>
#define ASAsyncTransactionAssertMainThread() NSAssert(0 != pthread_main_np(), @"This method must be called on the main thread"); #define ASAsyncTransactionAssertMainThread() NSAssert(0 != pthread_main_np(), @"This method must be called on the main thread");
NSInteger const ASDefaultTransactionPriority = 0; NSInteger const ASDefaultTransactionPriority = 0;
@interface ASDisplayNodeAsyncTransactionOperation : NSObject @interface ASAsyncTransactionOperation : NSObject
- (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock; - (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock;
@property (nonatomic, copy) asyncdisplaykit_async_transaction_operation_completion_block_t operationCompletionBlock; @property (nonatomic, copy) asyncdisplaykit_async_transaction_operation_completion_block_t operationCompletionBlock;
@property (nonatomic, strong) id<NSObject> value; // set on bg queue by the operation block @property (nonatomic, strong) id<NSObject> value; // set on bg queue by the operation block
@end @end
@implementation ASDisplayNodeAsyncTransactionOperation @implementation ASAsyncTransactionOperation
- (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock - (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock
{ {
@ -51,7 +55,7 @@ NSInteger const ASDefaultTransactionPriority = 0;
- (NSString *)description - (NSString *)description
{ {
return [NSString stringWithFormat:@"<ASDisplayNodeAsyncTransactionOperation: %p - value = %@", self, self.value]; return [NSString stringWithFormat:@"<ASAsyncTransactionOperation: %p - value = %@", self, self.value];
} }
@end @end
@ -317,7 +321,8 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
@implementation _ASAsyncTransaction @implementation _ASAsyncTransaction
{ {
ASAsyncTransactionQueue::Group *_group; ASAsyncTransactionQueue::Group *_group;
NSMutableArray *_operations; NSMutableArray<ASAsyncTransactionOperation *> *_operations;
_Atomic(ASAsyncTransactionState) _state;
} }
#pragma mark - #pragma mark -
@ -333,7 +338,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
_callbackQueue = callbackQueue; _callbackQueue = callbackQueue;
_completionBlock = [completionBlock copy]; _completionBlock = [completionBlock copy];
__atomic_store_n(&_state, ASAsyncTransactionStateOpen, __ATOMIC_SEQ_CST); _state = ATOMIC_VAR_INIT(ASAsyncTransactionStateOpen);
} }
return self; return self;
} }
@ -341,14 +346,25 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (void)dealloc - (void)dealloc
{ {
// Uncommitted transactions break our guarantees about releasing completion blocks on callbackQueue. // Uncommitted transactions break our guarantees about releasing completion blocks on callbackQueue.
NSAssert(__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateOpen, @"Uncommitted ASAsyncTransactions are not allowed"); NSAssert(self.state != ASAsyncTransactionStateOpen, @"Uncommitted ASAsyncTransactions are not allowed");
if (_group) { if (_group) {
_group->release(); _group->release();
} }
} }
#pragma mark - #pragma mark - Properties
#pragma mark Transaction Management
- (ASAsyncTransactionState)state
{
return atomic_load(&_state);
}
- (void)setState:(ASAsyncTransactionState)state
{
atomic_store(&_state, state);
}
#pragma mark - Transaction Management
- (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block - (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block
queue:(dispatch_queue_t)queue queue:(dispatch_queue_t)queue
@ -366,15 +382,15 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
{ {
ASAsyncTransactionAssertMainThread(); ASAsyncTransactionAssertMainThread();
NSAssert(__atomic_load_n(&_state, __ATOMIC_SEQ_CST) == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions"); NSAssert(self.state == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions");
[self _ensureTransactionData]; [self _ensureTransactionData];
ASDisplayNodeAsyncTransactionOperation *operation = [[ASDisplayNodeAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion]; ASAsyncTransactionOperation *operation = [[ASAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
[_operations addObject:operation]; [_operations addObject:operation];
_group->schedule(priority, queue, ^{ _group->schedule(priority, queue, ^{
@autoreleasepool { @autoreleasepool {
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateCanceled) { if (self.state != ASAsyncTransactionStateCanceled) {
_group->enter(); _group->enter();
block(^(id<NSObject> value){ block(^(id<NSObject> value){
operation.value = value; operation.value = value;
@ -401,15 +417,15 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
{ {
ASAsyncTransactionAssertMainThread(); ASAsyncTransactionAssertMainThread();
NSAssert(__atomic_load_n(&_state, __ATOMIC_SEQ_CST) == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions"); NSAssert(self.state == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions");
[self _ensureTransactionData]; [self _ensureTransactionData];
ASDisplayNodeAsyncTransactionOperation *operation = [[ASDisplayNodeAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion]; ASAsyncTransactionOperation *operation = [[ASAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
[_operations addObject:operation]; [_operations addObject:operation];
_group->schedule(priority, queue, ^{ _group->schedule(priority, queue, ^{
@autoreleasepool { @autoreleasepool {
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateCanceled) { if (self.state != ASAsyncTransactionStateCanceled) {
operation.value = block(); operation.value = block();
} }
} }
@ -428,15 +444,15 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (void)cancel - (void)cancel
{ {
ASAsyncTransactionAssertMainThread(); ASAsyncTransactionAssertMainThread();
NSAssert(__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateOpen, @"You can only cancel a committed or already-canceled transaction"); NSAssert(self.state != ASAsyncTransactionStateOpen, @"You can only cancel a committed or already-canceled transaction");
__atomic_store_n(&_state, ASAsyncTransactionStateCanceled, __ATOMIC_SEQ_CST); self.state = ASAsyncTransactionStateCanceled;
} }
- (void)commit - (void)commit
{ {
ASAsyncTransactionAssertMainThread(); ASAsyncTransactionAssertMainThread();
NSAssert(__atomic_load_n(&_state, __ATOMIC_SEQ_CST) == ASAsyncTransactionStateOpen, @"You cannot double-commit a transaction"); NSAssert(self.state == ASAsyncTransactionStateOpen, @"You cannot double-commit a transaction");
__atomic_store_n(&_state, ASAsyncTransactionStateCommitted, __ATOMIC_SEQ_CST); self.state = ASAsyncTransactionStateCommitted;
if ([_operations count] == 0) { if ([_operations count] == 0) {
// Fast path: if a transaction was opened, but no operations were added, execute completion block synchronously. // Fast path: if a transaction was opened, but no operations were added, execute completion block synchronously.
@ -457,16 +473,17 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (void)completeTransaction - (void)completeTransaction
{ {
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateComplete) { ASAsyncTransactionState state = self.state;
BOOL isCanceled = (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) == ASAsyncTransactionStateCanceled); if (state != ASAsyncTransactionStateComplete) {
for (ASDisplayNodeAsyncTransactionOperation *operation in _operations) { BOOL isCanceled = (state == ASAsyncTransactionStateCanceled);
for (ASAsyncTransactionOperation *operation in _operations) {
[operation callAndReleaseCompletionBlock:isCanceled]; [operation callAndReleaseCompletionBlock:isCanceled];
} }
// Always set _state to Complete, even if we were cancelled, to block any extraneous // Always set state to Complete, even if we were cancelled, to block any extraneous
// calls to this method that may have been scheduled for the next runloop // calls to this method that may have been scheduled for the next runloop
// (e.g. if we needed to force one in this runloop with -waitUntilComplete, but another was already scheduled) // (e.g. if we needed to force one in this runloop with -waitUntilComplete, but another was already scheduled)
__atomic_store_n(&_state, ASAsyncTransactionStateComplete, __ATOMIC_SEQ_CST); self.state = ASAsyncTransactionStateComplete;
if (_completionBlock) { if (_completionBlock) {
_completionBlock(self, isCanceled); _completionBlock(self, isCanceled);
@ -477,7 +494,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (void)waitUntilComplete - (void)waitUntilComplete
{ {
ASAsyncTransactionAssertMainThread(); ASAsyncTransactionAssertMainThread();
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateComplete) { if (self.state != ASAsyncTransactionStateComplete) {
if (_group) { if (_group) {
NSAssert(_callbackQueue == dispatch_get_main_queue(), nil); NSAssert(_callbackQueue == dispatch_get_main_queue(), nil);
_group->wait(); _group->wait();
@ -487,9 +504,9 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
// commit ourselves via the group to avoid double-committing the transaction. // commit ourselves via the group to avoid double-committing the transaction.
// This is only necessary when forcing display work to complete before allowing the runloop // This is only necessary when forcing display work to complete before allowing the runloop
// to continue, e.g. in the implementation of -[ASDisplayNode recursivelyEnsureDisplay]. // to continue, e.g. in the implementation of -[ASDisplayNode recursivelyEnsureDisplay].
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) == ASAsyncTransactionStateOpen) { if (self.state == ASAsyncTransactionStateOpen) {
[_ASAsyncTransactionGroup commit]; [_ASAsyncTransactionGroup commit];
NSAssert(__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateOpen, @"Transaction should not be open after committing group"); NSAssert(self.state != ASAsyncTransactionStateOpen, @"Transaction should not be open after committing group");
} }
// If we needed to commit the group above, -completeTransaction may have already been run. // If we needed to commit the group above, -completeTransaction may have already been run.
// It is designed to accommodate this by checking _state to ensure it is not complete. // It is designed to accommodate this by checking _state to ensure it is not complete.
@ -514,7 +531,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (NSString *)description - (NSString *)description
{ {
return [NSString stringWithFormat:@"<_ASAsyncTransaction: %p - _state = %lu, _group = %p, _operations = %@>", self, (unsigned long)__atomic_load_n(&_state, __ATOMIC_SEQ_CST), _group, _operations]; return [NSString stringWithFormat:@"<_ASAsyncTransaction: %p - _state = %lu, _group = %p, _operations = %@>", self, (unsigned long)self.state, _group, _operations];
} }
@end @end

View File

@ -10,11 +10,13 @@
#import <AsyncDisplayKit/_ASAsyncTransactionContainer.h> #import <AsyncDisplayKit/_ASAsyncTransactionContainer.h>
NS_ASSUME_NONNULL_BEGIN
@interface CALayer (ASAsyncTransactionContainerTransactions) @interface CALayer (ASAsyncTransactionContainerTransactions)
@property (nonatomic, strong, setter=asyncdisplaykit_setAsyncLayerTransactions:) NSHashTable *asyncdisplaykit_asyncLayerTransactions; @property (nonatomic, strong, nullable, setter=asyncdisplaykit_setAsyncLayerTransactions:) NSHashTable<_ASAsyncTransaction *> *asyncdisplaykit_asyncLayerTransactions;
@property (nonatomic, strong, setter=asyncdisplaykit_setCurrentAsyncLayerTransaction:) _ASAsyncTransaction *asyncdisplaykit_currentAsyncLayerTransaction;
- (void)asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:(_ASAsyncTransaction *)transaction; - (void)asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:(_ASAsyncTransaction *)transaction;
- (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction; - (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction;
@end @end
NS_ASSUME_NONNULL_END

View File

@ -10,6 +10,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class _ASAsyncTransaction; @class _ASAsyncTransaction;
@ -26,11 +27,11 @@ typedef NS_ENUM(NSUInteger, ASAsyncTransactionContainerState) {
ASAsyncTransactionContainerStatePendingTransactions, ASAsyncTransactionContainerStatePendingTransactions,
}; };
@protocol ASDisplayNodeAsyncTransactionContainer @protocol ASAsyncTransactionContainer
/** /**
@summary If YES, the receiver is marked as a container for async display, grouping all of the async display calls @summary If YES, the receiver is marked as a container for async transactions, grouping all of the transactions
in the layer hierarchy below the receiver together in a single ASAsyncTransaction. in the container hierarchy below the receiver together in a single ASAsyncTransaction.
@default NO @default NO
*/ */
@ -46,22 +47,26 @@ typedef NS_ENUM(NSUInteger, ASAsyncTransactionContainerState) {
*/ */
- (void)asyncdisplaykit_cancelAsyncTransactions; - (void)asyncdisplaykit_cancelAsyncTransactions;
@property (nonatomic, strong, nullable, setter=asyncdisplaykit_setCurrentAsyncTransaction:) _ASAsyncTransaction *asyncdisplaykit_currentAsyncTransaction;
@end @end
@interface CALayer (ASDisplayNodeAsyncTransactionContainer) <ASDisplayNodeAsyncTransactionContainer> @interface CALayer (ASAsyncTransactionContainer) <ASAsyncTransactionContainer>
/** /**
@summary Returns the current async transaction for this container layer. A new transaction is created if one @summary Returns the current async transaction for this layer. A new transaction is created if one
did not already exist. This method will always return an open, uncommitted transaction. did not already exist. This method will always return an open, uncommitted transaction.
@desc asyncdisplaykit_isAsyncTransactionContainer does not need to be YES for this to return a transaction. @desc asyncdisplaykit_isAsyncTransactionContainer does not need to be YES for this to return a transaction.
*/ */
@property (nonatomic, readonly, strong) _ASAsyncTransaction *asyncdisplaykit_asyncTransaction; @property (nonatomic, readonly, strong, nullable) _ASAsyncTransaction *asyncdisplaykit_asyncTransaction;
/** /**
@summary Goes up the superlayer chain until it finds the first layer with asyncdisplaykit_isAsyncTransactionContainer=YES (including the receiver) and returns it. @summary Goes up the superlayer chain until it finds the first layer with asyncdisplaykit_isAsyncTransactionContainer=YES (including the receiver) and returns it.
Returns nil if no parent container is found. Returns nil if no parent container is found.
*/ */
@property (nonatomic, readonly, strong) CALayer *asyncdisplaykit_parentTransactionContainer; @property (nonatomic, readonly, strong, nullable) CALayer *asyncdisplaykit_parentTransactionContainer;
@end @end
@interface UIView (ASDisplayNodeAsyncTransactionContainer) <ASDisplayNodeAsyncTransactionContainer> @interface UIView (ASAsyncTransactionContainer) <ASAsyncTransactionContainer>
@end @end
NS_ASSUME_NONNULL_END

View File

@ -19,16 +19,6 @@ static const char *ASDisplayNodeAssociatedCurrentTransactionKey = "ASAssociatedC
@implementation CALayer (ASAsyncTransactionContainerTransactions) @implementation CALayer (ASAsyncTransactionContainerTransactions)
- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncLayerTransaction
{
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey);
}
- (void)asyncdisplaykit_setCurrentAsyncLayerTransaction:(_ASAsyncTransaction *)transaction
{
objc_setAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey, transaction, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSHashTable *)asyncdisplaykit_asyncLayerTransactions - (NSHashTable *)asyncdisplaykit_asyncLayerTransactions
{ {
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey); return objc_getAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey);
@ -46,7 +36,17 @@ static const char *ASDisplayNodeAssociatedCurrentTransactionKey = "ASAssociatedC
static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"; static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer";
@implementation CALayer (ASDisplayNodeAsyncTransactionContainer) @implementation CALayer (ASAsyncTransactionContainer)
- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncTransaction
{
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey);
}
- (void)asyncdisplaykit_setCurrentAsyncTransaction:(_ASAsyncTransaction *)transaction
{
objc_setAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey, transaction, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)asyncdisplaykit_isAsyncTransactionContainer - (BOOL)asyncdisplaykit_isAsyncTransactionContainer
{ {
@ -70,9 +70,9 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
// If there was an open transaction, commit and clear the current transaction. Otherwise: // If there was an open transaction, commit and clear the current transaction. Otherwise:
// (1) The run loop observer will try to commit a canceled transaction which is not allowed // (1) The run loop observer will try to commit a canceled transaction which is not allowed
// (2) We leave the canceled transaction attached to the layer, dooming future operations // (2) We leave the canceled transaction attached to the layer, dooming future operations
_ASAsyncTransaction *currentTransaction = self.asyncdisplaykit_currentAsyncLayerTransaction; _ASAsyncTransaction *currentTransaction = self.asyncdisplaykit_currentAsyncTransaction;
[currentTransaction commit]; [currentTransaction commit];
self.asyncdisplaykit_currentAsyncLayerTransaction = nil; self.asyncdisplaykit_currentAsyncTransaction = nil;
for (_ASAsyncTransaction *transaction in [self.asyncdisplaykit_asyncLayerTransactions copy]) { for (_ASAsyncTransaction *transaction in [self.asyncdisplaykit_asyncLayerTransactions copy]) {
[transaction cancel]; [transaction cancel];
@ -81,7 +81,7 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
- (_ASAsyncTransaction *)asyncdisplaykit_asyncTransaction - (_ASAsyncTransaction *)asyncdisplaykit_asyncTransaction
{ {
_ASAsyncTransaction *transaction = self.asyncdisplaykit_currentAsyncLayerTransaction; _ASAsyncTransaction *transaction = self.asyncdisplaykit_currentAsyncTransaction;
if (transaction == nil) { if (transaction == nil) {
NSHashTable *transactions = self.asyncdisplaykit_asyncLayerTransactions; NSHashTable *transactions = self.asyncdisplaykit_asyncLayerTransactions;
if (transactions == nil) { if (transactions == nil) {
@ -98,7 +98,7 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
[self asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:completedTransaction]; [self asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:completedTransaction];
}]; }];
[transactions addObject:transaction]; [transactions addObject:transaction];
self.asyncdisplaykit_currentAsyncLayerTransaction = transaction; self.asyncdisplaykit_currentAsyncTransaction = transaction;
[self asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:transaction]; [self asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:transaction];
} }
[[_ASAsyncTransactionGroup mainTransactionGroup] addTransactionContainer:self]; [[_ASAsyncTransactionGroup mainTransactionGroup] addTransactionContainer:self];
@ -116,7 +116,7 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
@end @end
@implementation UIView (ASDisplayNodeAsyncTransactionContainer) @implementation UIView (ASAsyncTransactionContainer)
- (BOOL)asyncdisplaykit_isAsyncTransactionContainer - (BOOL)asyncdisplaykit_isAsyncTransactionContainer
{ {
@ -143,4 +143,14 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
// No-op in the base class. // No-op in the base class.
} }
- (void)asyncdisplaykit_setCurrentAsyncTransaction:(_ASAsyncTransaction *)transaction
{
self.layer.asyncdisplaykit_currentAsyncTransaction = transaction;
}
- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncTransaction
{
return self.layer.asyncdisplaykit_currentAsyncTransaction;
}
@end @end

View File

@ -8,19 +8,20 @@
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
// //
#import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN
@class _ASAsyncTransaction; @class _ASAsyncTransaction;
@protocol ASAsyncTransactionContainer;
/// A group of transaction container layers, for which the current transactions are committed together at the end of the next runloop tick. /// A group of transaction containers, for which the current transactions are committed together at the end of the next runloop tick.
@interface _ASAsyncTransactionGroup : NSObject @interface _ASAsyncTransactionGroup : NSObject
/// The main transaction group is scheduled to commit on every tick of the main runloop. /// The main transaction group is scheduled to commit on every tick of the main runloop.
+ (instancetype)mainTransactionGroup; + (_ASAsyncTransactionGroup *)mainTransactionGroup;
+ (void)commit; + (void)commit;
/// Add a transaction container to be committed. /// Add a transaction container to be committed.
/// @param containerLayer A layer containing a transaction to be committed. May or may not be a container layer.
/// @see ASAsyncTransactionContainer /// @see ASAsyncTransactionContainer
- (void)addTransactionContainer:(CALayer *)containerLayer; - (void)addTransactionContainer:(id<ASAsyncTransactionContainer>)container;
@end @end
NS_ASSUME_NONNULL_END

View File

@ -22,7 +22,7 @@ static void _transactionGroupRunLoopObserverCallback(CFRunLoopObserverRef observ
@end @end
@implementation _ASAsyncTransactionGroup { @implementation _ASAsyncTransactionGroup {
NSHashTable *_containerLayers; NSHashTable<id<ASAsyncTransactionContainer>> *_containers;
} }
+ (_ASAsyncTransactionGroup *)mainTransactionGroup + (_ASAsyncTransactionGroup *)mainTransactionGroup
@ -67,31 +67,31 @@ static void _transactionGroupRunLoopObserverCallback(CFRunLoopObserverRef observ
- (instancetype)init - (instancetype)init
{ {
if ((self = [super init])) { if ((self = [super init])) {
_containerLayers = [NSHashTable hashTableWithOptions:NSPointerFunctionsObjectPointerPersonality]; _containers = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
} }
return self; return self;
} }
- (void)addTransactionContainer:(CALayer *)containerLayer - (void)addTransactionContainer:(id<ASAsyncTransactionContainer>)container
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(containerLayer != nil, @"No container"); ASDisplayNodeAssert(container != nil, @"No container");
[_containerLayers addObject:containerLayer]; [_containers addObject:container];
} }
- (void)commit - (void)commit
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if ([_containerLayers count]) { if ([_containers count]) {
NSHashTable *containerLayersToCommit = _containerLayers; NSHashTable *containersToCommit = _containers;
_containerLayers = [NSHashTable hashTableWithOptions:NSPointerFunctionsObjectPointerPersonality]; _containers = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
for (CALayer *containerLayer in containerLayersToCommit) { for (id<ASAsyncTransactionContainer> container in containersToCommit) {
// Note that the act of committing a transaction may open a new transaction, // Note that the act of committing a transaction may open a new transaction,
// so we must nil out the transaction we're committing first. // so we must nil out the transaction we're committing first.
_ASAsyncTransaction *transaction = containerLayer.asyncdisplaykit_currentAsyncLayerTransaction; _ASAsyncTransaction *transaction = container.asyncdisplaykit_currentAsyncTransaction;
containerLayer.asyncdisplaykit_currentAsyncLayerTransaction = nil; container.asyncdisplaykit_currentAsyncTransaction = nil;
[transaction commit]; [transaction commit];
} }
} }

View File

@ -974,4 +974,14 @@ nodeProperty = nodeValueExpr; _setToViewOnly(viewAndPendingViewStateProperty, vi
[_layer asyncdisplaykit_cancelAsyncTransactions]; [_layer asyncdisplaykit_cancelAsyncTransactions];
} }
- (void)asyncdisplaykit_setCurrentAsyncTransaction:(_ASAsyncTransaction *)transaction
{
_layer.asyncdisplaykit_currentAsyncTransaction = transaction;
}
- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncTransaction
{
return _layer.asyncdisplaykit_currentAsyncTransaction;
}
@end @end