[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.
* See: -(void)asyncdisplaykit_asyncTransactionContainerStateDidChange in ASDisplayNodeSubclass.h
*/
@interface ASDisplayNode (ASDisplayNodeAsyncTransactionContainer) <ASDisplayNodeAsyncTransactionContainer>
@interface ASDisplayNode (ASAsyncTransactionContainer) <ASAsyncTransactionContainer>
@end
/** UIVIew(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to an UIView. */

View File

@ -10,14 +10,16 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#define ASDISPLAYNODE_DELAY_DISPLAY 0
@class _ASAsyncTransaction;
typedef void(^asyncdisplaykit_async_transaction_completion_block_t)(_ASAsyncTransaction *completedTransaction, BOOL canceled);
typedef id<NSObject>(^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_complete_async_operation_block_t)(id<NSObject> value);
typedef id<NSObject> _Nullable(^asyncdisplaykit_async_transaction_operation_block_t)(void);
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> _Nullable value);
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.
@see initWithCallbackQueue:commitBlock:completionBlock:executeConcurrently:
@param callbackQueue The dispatch queue that the completion blocks will be called on.
@param completionBlock A block that is called when the transaction is completed. May be NULL.
@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.
*/
- (instancetype)initWithCallbackQueue:(dispatch_queue_t)callbackQueue
completionBlock:(asyncdisplaykit_async_transaction_completion_block_t)completionBlock;
- (instancetype)initWithCallbackQueue:(nullable dispatch_queue_t)callbackQueue
completionBlock:(nullable asyncdisplaykit_async_transaction_completion_block_t)completionBlock;
/**
@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.
*/
@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.
*/
@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.
@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.
@ -97,7 +99,7 @@ extern NSInteger const ASDefaultTransactionPriority;
*/
- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block
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.
@ -115,7 +117,7 @@ extern NSInteger const ASDefaultTransactionPriority;
- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block
priority:(NSInteger)priority
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
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.
@ -154,7 +156,7 @@ extern NSInteger const ASDefaultTransactionPriority;
- (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block
priority:(NSInteger)priority
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;
@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.
//
// We need this import for UITrackingRunLoopMode
#import <UIKit/UIApplication.h>
#import "_ASAsyncTransaction.h"
#import "_ASAsyncTransactionGroup.h"
#import <list>
#import <map>
#import <mutex>
#import <stdatomic.h>
#define ASAsyncTransactionAssertMainThread() NSAssert(0 != pthread_main_np(), @"This method must be called on the main thread");
NSInteger const ASDefaultTransactionPriority = 0;
@interface ASDisplayNodeAsyncTransactionOperation : NSObject
@interface ASAsyncTransactionOperation : NSObject
- (instancetype)initWithOperationCompletionBlock:(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
@end
@implementation ASDisplayNodeAsyncTransactionOperation
@implementation ASAsyncTransactionOperation
- (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock
{
@ -51,7 +55,7 @@ NSInteger const ASDefaultTransactionPriority = 0;
- (NSString *)description
{
return [NSString stringWithFormat:@"<ASDisplayNodeAsyncTransactionOperation: %p - value = %@", self, self.value];
return [NSString stringWithFormat:@"<ASAsyncTransactionOperation: %p - value = %@", self, self.value];
}
@end
@ -317,7 +321,8 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
@implementation _ASAsyncTransaction
{
ASAsyncTransactionQueue::Group *_group;
NSMutableArray *_operations;
NSMutableArray<ASAsyncTransactionOperation *> *_operations;
_Atomic(ASAsyncTransactionState) _state;
}
#pragma mark -
@ -333,7 +338,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
_callbackQueue = callbackQueue;
_completionBlock = [completionBlock copy];
__atomic_store_n(&_state, ASAsyncTransactionStateOpen, __ATOMIC_SEQ_CST);
_state = ATOMIC_VAR_INIT(ASAsyncTransactionStateOpen);
}
return self;
}
@ -341,14 +346,25 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (void)dealloc
{
// 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) {
_group->release();
}
}
#pragma mark -
#pragma mark Transaction Management
#pragma mark - Properties
- (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
queue:(dispatch_queue_t)queue
@ -366,15 +382,15 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
{
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];
ASDisplayNodeAsyncTransactionOperation *operation = [[ASDisplayNodeAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
ASAsyncTransactionOperation *operation = [[ASAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
[_operations addObject:operation];
_group->schedule(priority, queue, ^{
@autoreleasepool {
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateCanceled) {
if (self.state != ASAsyncTransactionStateCanceled) {
_group->enter();
block(^(id<NSObject> value){
operation.value = value;
@ -401,15 +417,15 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
{
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];
ASDisplayNodeAsyncTransactionOperation *operation = [[ASDisplayNodeAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
ASAsyncTransactionOperation *operation = [[ASAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
[_operations addObject:operation];
_group->schedule(priority, queue, ^{
@autoreleasepool {
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateCanceled) {
if (self.state != ASAsyncTransactionStateCanceled) {
operation.value = block();
}
}
@ -428,15 +444,15 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (void)cancel
{
ASAsyncTransactionAssertMainThread();
NSAssert(__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateOpen, @"You can only cancel a committed or already-canceled transaction");
__atomic_store_n(&_state, ASAsyncTransactionStateCanceled, __ATOMIC_SEQ_CST);
NSAssert(self.state != ASAsyncTransactionStateOpen, @"You can only cancel a committed or already-canceled transaction");
self.state = ASAsyncTransactionStateCanceled;
}
- (void)commit
{
ASAsyncTransactionAssertMainThread();
NSAssert(__atomic_load_n(&_state, __ATOMIC_SEQ_CST) == ASAsyncTransactionStateOpen, @"You cannot double-commit a transaction");
__atomic_store_n(&_state, ASAsyncTransactionStateCommitted, __ATOMIC_SEQ_CST);
NSAssert(self.state == ASAsyncTransactionStateOpen, @"You cannot double-commit a transaction");
self.state = ASAsyncTransactionStateCommitted;
if ([_operations count] == 0) {
// 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
{
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateComplete) {
BOOL isCanceled = (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) == ASAsyncTransactionStateCanceled);
for (ASDisplayNodeAsyncTransactionOperation *operation in _operations) {
ASAsyncTransactionState state = self.state;
if (state != ASAsyncTransactionStateComplete) {
BOOL isCanceled = (state == ASAsyncTransactionStateCanceled);
for (ASAsyncTransactionOperation *operation in _operations) {
[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
// (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) {
_completionBlock(self, isCanceled);
@ -477,7 +494,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (void)waitUntilComplete
{
ASAsyncTransactionAssertMainThread();
if (__atomic_load_n(&_state, __ATOMIC_SEQ_CST) != ASAsyncTransactionStateComplete) {
if (self.state != ASAsyncTransactionStateComplete) {
if (_group) {
NSAssert(_callbackQueue == dispatch_get_main_queue(), nil);
_group->wait();
@ -487,9 +504,9 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
// 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
// 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];
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.
// It is designed to accommodate this by checking _state to ensure it is not complete.
@ -514,7 +531,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
- (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

View File

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

View File

@ -10,6 +10,7 @@
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class _ASAsyncTransaction;
@ -26,11 +27,11 @@ typedef NS_ENUM(NSUInteger, ASAsyncTransactionContainerState) {
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
in the layer hierarchy below the receiver together in a single ASAsyncTransaction.
@summary If YES, the receiver is marked as a container for async transactions, grouping all of the transactions
in the container hierarchy below the receiver together in a single ASAsyncTransaction.
@default NO
*/
@ -46,22 +47,26 @@ typedef NS_ENUM(NSUInteger, ASAsyncTransactionContainerState) {
*/
- (void)asyncdisplaykit_cancelAsyncTransactions;
@property (nonatomic, strong, nullable, setter=asyncdisplaykit_setCurrentAsyncTransaction:) _ASAsyncTransaction *asyncdisplaykit_currentAsyncTransaction;
@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.
@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.
Returns nil if no parent container is found.
*/
@property (nonatomic, readonly, strong) CALayer *asyncdisplaykit_parentTransactionContainer;
@property (nonatomic, readonly, strong, nullable) CALayer *asyncdisplaykit_parentTransactionContainer;
@end
@interface UIView (ASDisplayNodeAsyncTransactionContainer) <ASDisplayNodeAsyncTransactionContainer>
@interface UIView (ASAsyncTransactionContainer) <ASAsyncTransactionContainer>
@end
NS_ASSUME_NONNULL_END

View File

@ -19,16 +19,6 @@ static const char *ASDisplayNodeAssociatedCurrentTransactionKey = "ASAssociatedC
@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
{
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey);
@ -46,7 +36,17 @@ static const char *ASDisplayNodeAssociatedCurrentTransactionKey = "ASAssociatedC
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
{
@ -70,9 +70,9 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
// 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
// (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];
self.asyncdisplaykit_currentAsyncLayerTransaction = nil;
self.asyncdisplaykit_currentAsyncTransaction = nil;
for (_ASAsyncTransaction *transaction in [self.asyncdisplaykit_asyncLayerTransactions copy]) {
[transaction cancel];
@ -81,7 +81,7 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
- (_ASAsyncTransaction *)asyncdisplaykit_asyncTransaction
{
_ASAsyncTransaction *transaction = self.asyncdisplaykit_currentAsyncLayerTransaction;
_ASAsyncTransaction *transaction = self.asyncdisplaykit_currentAsyncTransaction;
if (transaction == nil) {
NSHashTable *transactions = self.asyncdisplaykit_asyncLayerTransactions;
if (transactions == nil) {
@ -98,7 +98,7 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
[self asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:completedTransaction];
}];
[transactions addObject:transaction];
self.asyncdisplaykit_currentAsyncLayerTransaction = transaction;
self.asyncdisplaykit_currentAsyncTransaction = transaction;
[self asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:transaction];
}
[[_ASAsyncTransactionGroup mainTransactionGroup] addTransactionContainer:self];
@ -116,7 +116,7 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
@end
@implementation UIView (ASDisplayNodeAsyncTransactionContainer)
@implementation UIView (ASAsyncTransactionContainer)
- (BOOL)asyncdisplaykit_isAsyncTransactionContainer
{
@ -143,4 +143,14 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer"
// 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

View File

@ -8,19 +8,20 @@
// of patent rights can be found in the PATENTS file in the same directory.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@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
/// The main transaction group is scheduled to commit on every tick of the main runloop.
+ (instancetype)mainTransactionGroup;
+ (_ASAsyncTransactionGroup *)mainTransactionGroup;
+ (void)commit;
/// 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
- (void)addTransactionContainer:(CALayer *)containerLayer;
- (void)addTransactionContainer:(id<ASAsyncTransactionContainer>)container;
@end
NS_ASSUME_NONNULL_END

View File

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

View File

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