From 929d118c962dcf595386daf5c91ce30e1f8e577d Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 16 Jul 2017 16:59:28 -0700 Subject: [PATCH 01/22] fix SIMULATE_WEB_RESPONSE not imported #449 --- examples/ASCollectionView/Sample/ViewController.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index d00ea60df..4e6a04e97 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -16,7 +16,7 @@ // #import "ViewController.h" - +#import "AppDelegate.h" #import #import "SupplementaryNode.h" #import "ItemNode.h" @@ -70,8 +70,8 @@ - (void)viewDidLoad { NSLog(@"ViewController is not nil"); strongSelf->_data = [[NSArray alloc] init]; - [strongSelf->_collectionView performBatchUpdates:^{ - [strongSelf->_collectionView insertSections:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, 100)]]; + [strongSelf->_collectionNode performBatchUpdates:^{ + [strongSelf->_collectionNode insertSections:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, 100)]]; } completion:nil]; NSLog(@"ViewController finished updating collectionView"); } @@ -81,7 +81,7 @@ - (void)viewDidLoad }; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), mockWebService); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.navigationController popViewControllerAnimated:YES]; }); #endif From ee44725d6036ad06dd03d286de14382bdc2e7346 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Wed, 10 Jan 2018 17:02:20 +0800 Subject: [PATCH 02/22] Coalesce interface state updates to ASCATransactionQueue before CATransaction commit. This will avoid duplicate interface state delegate calls caused by view repeatly added/removed to/from hierarchy during controller animation transition. --- Source/ASDisplayNode.mm | 39 ++- Source/ASRunLoopQueue.h | 23 ++ Source/ASRunLoopQueue.mm | 257 ++++++++++++++++++++ Source/Private/ASDisplayNodeInternal.h | 2 +- Tests/ASDisplayNodeImplicitHierarchyTests.m | 1 + Tests/ASDisplayNodeTests.mm | 19 +- Tests/ASDisplayNodeTestsHelper.h | 1 + Tests/ASDisplayNodeTestsHelper.m | 12 + Tests/ASVideoNodeTests.m | 3 +- 9 files changed, 341 insertions(+), 16 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 3a537ca4b..0da1dd6c5 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -62,7 +62,7 @@ // We have to forward declare the protocol as this place otherwise it will not compile compiling with an Base SDK < iOS 10 @protocol CALayerDelegate; -@interface ASDisplayNode () +@interface ASDisplayNode () /** * See ASDisplayNodeInternal.h for ivars @@ -2741,7 +2741,9 @@ - (void)setHierarchyState:(ASHierarchyState)newState // Entered or exited range managed state. if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) { if (newState & ASHierarchyStateRangeManaged) { - [self enterInterfaceState:self.supernode.interfaceState]; + if (self.supernode) { + [self enterInterfaceState:self.supernode->_pendingInterfaceState]; + } } else { // The case of exiting a range-managed state should be fairly rare. Adding or removing the node // to a view hierarchy will cause its interfaceState to be either fully set or unset (all fields), @@ -2795,7 +2797,7 @@ - (void)didExitHierarchy // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. - if (ASInterfaceStateIncludesVisible(self.interfaceState)) { + if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { dispatch_async(dispatch_get_main_queue(), ^{ // This block intentionally retains self. __instanceLock__.lock(); @@ -2868,25 +2870,43 @@ - (ASInterfaceState)interfaceState } - (void)setInterfaceState:(ASInterfaceState)newState +{ + ASDN::MutexLocker l(__instanceLock__); + if (!self.automaticallyManagesSubnodes) { + if (_pendingInterfaceState != newState) { + _pendingInterfaceState = newState; + [[ASCATransactionQueue sharedQueue] enqueue:self]; + } + } else { + _pendingInterfaceState = newState; + [self applyPendingInterfaceState]; + } +} + +- (void)applyPendingInterfaceState { //This method is currently called on the main thread. The assert has been added here because all of the //did(Enter|Exit)(Display|Visible|Preload)State methods currently guarantee calling on main. ASDisplayNodeAssertMainThread(); - // It should never be possible for a node to be visible but not be allowed / expected to display. - ASDisplayNodeAssertFalse(ASInterfaceStateIncludesVisible(newState) && !ASInterfaceStateIncludesDisplay(newState)); + // This method manages __instanceLock__ itself, to ensure the lock is not held while didEnter/Exit(.*)State methods are called, thus avoid potential deadlocks ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); ASInterfaceState oldState = ASInterfaceStateNone; + ASInterfaceState newState = ASInterfaceStateNone; { ASDN::MutexLocker l(__instanceLock__); - if (_interfaceState == newState) { + oldState = _interfaceState; + newState = _pendingInterfaceState; + if (newState == oldState) { return; } - oldState = _interfaceState; _interfaceState = newState; } + // It should never be possible for a node to be visible but not be allowed / expected to display. + ASDisplayNodeAssertFalse(ASInterfaceStateIncludesVisible(newState) && !ASInterfaceStateIncludesDisplay(newState)); + // TODO: Trigger asynchronous measurement if it is not already cached or being calculated. // if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) { // } @@ -2983,6 +3003,11 @@ - (void)setInterfaceState:(ASInterfaceState)newState [self interfaceStateDidChange:newState fromState:oldState]; } +- (void)prepareForCATransactionCommit +{ + [self applyPendingInterfaceState]; +} + - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState { // Subclass hook diff --git a/Source/ASRunLoopQueue.h b/Source/ASRunLoopQueue.h index 50a0eeb24..3ac5d7bc8 100644 --- a/Source/ASRunLoopQueue.h +++ b/Source/ASRunLoopQueue.h @@ -20,6 +20,10 @@ NS_ASSUME_NONNULL_BEGIN +@protocol ASCATransactionQueueObserving +- (void)prepareForCATransactionCommit; +@end + AS_SUBCLASSING_RESTRICTED @interface ASRunLoopQueue : NSObject @@ -48,6 +52,25 @@ AS_SUBCLASSING_RESTRICTED @end +AS_SUBCLASSING_RESTRICTED +@interface ASCATransactionQueue : NSObject + +@property (nonatomic, readonly) BOOL isEmpty; + +/** + * The queue to run on main run loop before CATransaction commit. + * + * @discussion this queue will run after ASRunLoopQueue and before CATransaction commit + * to get last chance of updating/coalesce info like interface state. + * Each node will only be called once per transaction commit to reflect interface change. + */ ++ (ASCATransactionQueue *)sharedQueue; + +- (void)enqueue:(id)object; + +@end + + AS_SUBCLASSING_RESTRICTED @interface ASDeallocQueue : NSObject diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index ced798252..c511e07a3 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -469,3 +469,260 @@ - (void)unlock } @end + +#pragma mark - ASCATransactionQueue + +@interface ASCATransactionQueue () { + CFRunLoopRef _runLoop; + CFRunLoopSourceRef _runLoopSource; + CFRunLoopObserverRef _runLoopObserver; + NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance. + ASDN::RecursiveMutex _internalQueueLock; + + // In order to not pollute the top-level activities, each queue has 1 root activity. + os_activity_t _rootActivity; + +#if ASRunLoopQueueLoggingEnabled + NSTimer *_runloopQueueLoggingTimer; +#endif +} + +@end + +@implementation ASCATransactionQueue + +// CoreAnimation commit order is 2000000, the goal of this is to process shortly beforehand +// but after most other scheduled work on the runloop has processed. +static int const kASASCATransactionQueueOrder = 1000000; + +#if AS_KDEBUG_ENABLE ++ (void)load +{ + [self registerCATransactionObservers]; +} + ++ (void)registerCATransactionObservers +{ + static BOOL privateCAMethodsExist; + static dispatch_block_t preLayoutHandler; + static dispatch_block_t preCommitHandler; + static dispatch_block_t postCommitHandler; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + privateCAMethodsExist = [CATransaction respondsToSelector:@selector(addCommitHandler:forPhase:)]; + privateCAMethodsExist &= [CATransaction respondsToSelector:@selector(currentState)]; + if (!privateCAMethodsExist) { + NSLog(@"Private CA methods are gone."); + } + preLayoutHandler = ^{ + ASSignpostStartCustom(ASSignpostCATransactionLayout, 0, [CATransaction currentState]); + }; + preCommitHandler = ^{ + int state = [CATransaction currentState]; + ASSignpostEndCustom(ASSignpostCATransactionLayout, 0, state, ASSignpostColorDefault); + ASSignpostStartCustom(ASSignpostCATransactionCommit, 0, state); + }; + postCommitHandler = ^{ + ASSignpostEndCustom(ASSignpostCATransactionCommit, 0, [CATransaction currentState], ASSignpostColorDefault); + // Can't add new observers inside an observer. rdar://problem/31253952 + dispatch_async(dispatch_get_main_queue(), ^{ + [self registerCATransactionObservers]; + }); + }; + }); + + if (privateCAMethodsExist) { + [CATransaction addCommitHandler:preLayoutHandler forPhase:kCATransactionPhasePreLayout]; + [CATransaction addCommitHandler:preCommitHandler forPhase:kCATransactionPhasePreCommit]; + [CATransaction addCommitHandler:postCommitHandler forPhase:kCATransactionPhasePostCommit]; + } +} + +#endif // AS_KDEBUG_ENABLE + ++ (ASCATransactionQueue *)sharedQueue +{ + static dispatch_once_t onceToken; + static ASCATransactionQueue *sharedQueue; + dispatch_once(&onceToken, ^{ + sharedQueue = [[ASCATransactionQueue alloc] init]; + }); + return sharedQueue; +} + +- (instancetype)init +{ + if (self = [super init]) { + _runLoop = CFRunLoopGetMain(); + NSPointerFunctionsOptions options = NSPointerFunctionsStrongMemory; + _internalQueue = [[NSPointerArray alloc] initWithOptions:options]; + + // We don't want to pollute the top-level app activities with run loop batches, so we create one top-level + // activity per queue, and each batch activity joins that one instead. + _rootActivity = as_activity_create("Process run loop queue items", OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DEFAULT); + { + // Log a message identifying this queue into the queue's root activity. + as_activity_scope_verbose(_rootActivity); + as_log_verbose(ASDisplayLog(), "Created run loop queue: %@", self); + } + + // Self is guaranteed to outlive the observer. Without the high cost of a weak pointer, + // __unsafe_unretained allows us to avoid flagging the memory cycle detector. + __unsafe_unretained __typeof__(self) weakSelf = self; + void (^handlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + [weakSelf processQueue]; + }; + _runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueueOrder, handlerBlock); + CFRunLoopAddObserver(_runLoop, _runLoopObserver, kCFRunLoopCommonModes); + + // It is not guaranteed that the runloop will turn if it has no scheduled work, and this causes processing of + // the queue to stop. Attaching a custom loop source to the run loop and signal it if new work needs to be done + CFRunLoopSourceContext sourceContext = {}; + sourceContext.perform = runLoopSourceCallback; +#if ASRunLoopQueueLoggingEnabled + sourceContext.info = (__bridge void *)self; +#endif + _runLoopSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes); + +#if ASRunLoopQueueLoggingEnabled + _runloopQueueLoggingTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(checkRunLoop) userInfo:nil repeats:YES]; + [[NSRunLoop mainRunLoop] addTimer:_runloopQueueLoggingTimer forMode:NSRunLoopCommonModes]; +#endif + } + return self; +} + +- (void)dealloc +{ + if (CFRunLoopContainsSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes)) { + CFRunLoopRemoveSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes); + } + CFRelease(_runLoopSource); + _runLoopSource = nil; + + if (CFRunLoopObserverIsValid(_runLoopObserver)) { + CFRunLoopObserverInvalidate(_runLoopObserver); + } + CFRelease(_runLoopObserver); + _runLoopObserver = nil; +} + +#if ASRunLoopQueueLoggingEnabled +- (void)checkRunLoop +{ + NSLog(@"<%@> - Jobs: %ld", self, _internalQueue.size()); +} +#endif + +- (void)processQueue +{ + // If we have an execution block, this vector will be populated, otherwise remains empty. + // This is to avoid needlessly retaining/releasing the objects if we don't have a block. + std::vector itemsToProcess; + + BOOL isQueueDrained = NO; + { + ASDN::MutexLocker l(_internalQueueLock); + + NSInteger internalQueueCount = _internalQueue.count; + // Early-exit if the queue is empty. + if (internalQueueCount == 0) { + return; + } + + ASSignpostStart(ASSignpostRunLoopQueueBatch); + + /** + * For each item in the next batch, if it's non-nil then NULL it out + * and if we have an execution block then add it in. + * This could be written a bunch of different ways but + * this particular one nicely balances readability, safety, and efficiency. + */ + NSInteger foundItemCount = 0; + for (NSInteger i = 0; i < internalQueueCount && foundItemCount < internalQueueCount; i++) { + /** + * It is safe to use unsafe_unretained here. If the queue is weak, the + * object will be added to the autorelease pool. If the queue is strong, + * it will retain the object until we transfer it (retain it) in itemsToProcess. + */ + __unsafe_unretained id ptr = (__bridge id)[_internalQueue pointerAtIndex:i]; + if (ptr != nil) { + foundItemCount++; + itemsToProcess.push_back(ptr); + [_internalQueue replacePointerAtIndex:i withPointer:NULL]; + } + } + + if (foundItemCount == 0) { + // If _internalQueue holds weak references, and all of them just become NULL, then the array + // is never marked as needsCompletion, and compact will return early, not removing the NULL's. + // Inserting a NULL here ensures the compaction will take place. + // See http://www.openradar.me/15396578 and https://stackoverflow.com/a/40274426/1136669 + [_internalQueue addPointer:NULL]; + } + + [_internalQueue compact]; + if (_internalQueue.count == 0) { + isQueueDrained = YES; + } + } + + // itemsToProcess will be empty if _queueConsumer == nil so no need to check again. + auto count = itemsToProcess.size(); + if (count > 0) { + as_activity_scope_verbose(as_activity_create("Process run loop queue batch", _rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + auto itemsEnd = itemsToProcess.cend(); + for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) { + __unsafe_unretained id value = *iterator; + [value prepareForCATransactionCommit]; + // _queueConsumer(value, isQueueDrained && iterator == itemsEnd - 1); + as_log_verbose(ASDisplayLog(), "processed %@", value); + } + if (count > 1) { + as_log_verbose(ASDisplayLog(), "processed %lu items", (unsigned long)count); + } + } + + // If the queue is not fully drained yet force another run loop to process next batch of items + if (!isQueueDrained) { + CFRunLoopSourceSignal(_runLoopSource); + CFRunLoopWakeUp(_runLoop); + } + + ASSignpostEnd(ASSignpostRunLoopQueueBatch); +} + +- (void)enqueue:(id)object +{ + if (!object) { + return; + } + + ASDN::MutexLocker l(_internalQueueLock); + + // Check if the object exists. + BOOL foundObject = NO; + + for (id currentObject in _internalQueue) { + if (currentObject == object) { + foundObject = YES; + break; + } + } + + if (!foundObject) { + [_internalQueue addPointer:(__bridge void *)object]; + + CFRunLoopSourceSignal(_runLoopSource); + CFRunLoopWakeUp(_runLoop); + } +} + +- (BOOL)isEmpty +{ + ASDN::MutexLocker l(_internalQueueLock); + return _internalQueue.count == 0; +} + +@end diff --git a/Source/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h index 1be9e8c94..545f1c5a2 100644 --- a/Source/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -77,7 +77,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo { @package _ASPendingState *_pendingViewState; - + ASInterfaceState _pendingInterfaceState; UIView *_view; CALayer *_layer; diff --git a/Tests/ASDisplayNodeImplicitHierarchyTests.m b/Tests/ASDisplayNodeImplicitHierarchyTests.m index 11e2ce4af..18cb8bf8b 100644 --- a/Tests/ASDisplayNodeImplicitHierarchyTests.m +++ b/Tests/ASDisplayNodeImplicitHierarchyTests.m @@ -130,6 +130,7 @@ - (void)testInitialNodeInsertionWhenEnterPreloadState ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))); [node recursivelySetInterfaceState:ASInterfaceStatePreload]; + ASCATransactionQueueWait(); // No premature view allocation XCTAssertFalse(node.isNodeLoaded); // Subnodes should be inserted, laid out and entered preload state diff --git a/Tests/ASDisplayNodeTests.mm b/Tests/ASDisplayNodeTests.mm index df4adbec6..45587c0a7 100644 --- a/Tests/ASDisplayNodeTests.mm +++ b/Tests/ASDisplayNodeTests.mm @@ -91,7 +91,7 @@ @interface ASDisplayNode (HackForTests) - (id)initWithViewClass:(Class)viewClass; - (id)initWithLayerClass:(Class)layerClass; - +- (void)setInterfaceState:(ASInterfaceState)state; // FIXME: Importing ASDisplayNodeInternal.h causes a heap of problems. - (void)enterInterfaceState:(ASInterfaceState)interfaceState; @end @@ -122,6 +122,12 @@ @interface ASTestResponderNode : ASTestDisplayNode @implementation ASTestDisplayNode +- (void)setInterfaceState:(ASInterfaceState)state +{ + [super setInterfaceState:state]; + ASCATransactionQueueWait(); +} + - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { return _calculateSizeBlock ? _calculateSizeBlock(self, constrainedSize) : CGSizeZero; @@ -2036,9 +2042,9 @@ - (void)testThatNodeGetsRenderedIfItGoesFromZeroSizeToRealSizeButOnlyOnce // Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2205 - (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenContainerEntersHierarchy { - ASDisplayNode *supernode = [[ASDisplayNode alloc] init]; + ASDisplayNode *supernode = [[ASTestDisplayNode alloc] init]; [supernode enableSubtreeRasterization]; - ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; + ASDisplayNode *subnode = [[ASTestDisplayNode alloc] init]; ASSetDebugNames(supernode, subnode); UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; [supernode addSubnode:subnode]; @@ -2054,9 +2060,9 @@ - (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenContainerEntersHierar // Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2205 - (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenAddedToContainerThatIsInHierarchy { - ASDisplayNode *supernode = [[ASDisplayNode alloc] init]; + ASDisplayNode *supernode = [[ASTestDisplayNode alloc] init]; [supernode enableSubtreeRasterization]; - ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; + ASDisplayNode *subnode = [[ASTestDisplayNode alloc] init]; ASSetDebugNames(supernode, subnode); UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; @@ -2170,8 +2176,7 @@ - (void)testThatSubnodeGetsInterfaceStateSetIfRasterized [node view]; // Node needs to be loaded [node enterInterfaceState:ASInterfaceStatePreload]; - - + XCTAssertTrue((node.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload); XCTAssertTrue((subnode.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload); XCTAssertTrue(node.hasPreloaded); diff --git a/Tests/ASDisplayNodeTestsHelper.h b/Tests/ASDisplayNodeTestsHelper.h index 447350004..98b4719c6 100644 --- a/Tests/ASDisplayNodeTestsHelper.h +++ b/Tests/ASDisplayNodeTestsHelper.h @@ -28,5 +28,6 @@ BOOL ASDisplayNodeRunRunLoopUntilBlockIsTrue(as_condition_block_t block); void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size); void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange); +void ASCATransactionQueueWait(void); ASDISPLAYNODE_EXTERN_C_END diff --git a/Tests/ASDisplayNodeTestsHelper.m b/Tests/ASDisplayNodeTestsHelper.m index 05397b83b..0a37da1e4 100644 --- a/Tests/ASDisplayNodeTestsHelper.m +++ b/Tests/ASDisplayNodeTestsHelper.m @@ -18,6 +18,7 @@ #import "ASDisplayNodeTestsHelper.h" #import #import +#import #import @@ -62,3 +63,14 @@ void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange) CGSize sizeThatFits = [node layoutThatFits:sizeRange].size; node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits}; } + +void ASCATransactionQueueWait(void) +{ + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:1]; + BOOL whileResult = YES; + while ([date timeIntervalSinceNow] > 0 && + (whileResult = ![[ASCATransactionQueue sharedQueue] isEmpty])) { + [[NSRunLoop currentRunLoop] runUntilDate: + [NSDate dateWithTimeIntervalSinceNow:0.01]]; + } +} diff --git a/Tests/ASVideoNodeTests.m b/Tests/ASVideoNodeTests.m index 278509efe..96892b176 100644 --- a/Tests/ASVideoNodeTests.m +++ b/Tests/ASVideoNodeTests.m @@ -21,6 +21,7 @@ #import #import #import +#import "ASDisplayNodeTestsHelper.h" @interface ASVideoNodeTests : XCTestCase { @@ -351,9 +352,9 @@ - (void)testVideoResumedWhenBufferIsLikelyToKeepUp [_videoNode setInterfaceState:ASInterfaceStateVisible | ASInterfaceStateDisplay | ASInterfaceStatePreload]; [_videoNode prepareToPlayAsset:assetMock withKeys:_requestedKeys]; + ASCATransactionQueueWait(); [_videoNode pause]; _videoNode.shouldBePlaying = YES; - XCTAssertFalse(_videoNode.isPlaying); [_videoNode observeValueForKeyPath:@"playbackLikelyToKeepUp" ofObject:[_videoNode currentItem] change:@{NSKeyValueChangeNewKey : @YES} context:NULL]; From cdd2e9bbe3ef4751435d17f913a78abd59b17e6f Mon Sep 17 00:00:00 2001 From: Max Wang Date: Fri, 12 Jan 2018 10:11:09 +0800 Subject: [PATCH 03/22] fix tests for new run loop queue --- Source/ASDisplayNode.mm | 5 ----- Source/ASRunLoopQueue.mm | 1 - Tests/ASCollectionViewTests.mm | 4 ++++ Tests/ASDisplayNodeImplicitHierarchyTests.m | 1 + 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 0da1dd6c5..83f1c7fef 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2872,15 +2872,10 @@ - (ASInterfaceState)interfaceState - (void)setInterfaceState:(ASInterfaceState)newState { ASDN::MutexLocker l(__instanceLock__); - if (!self.automaticallyManagesSubnodes) { if (_pendingInterfaceState != newState) { _pendingInterfaceState = newState; [[ASCATransactionQueue sharedQueue] enqueue:self]; } - } else { - _pendingInterfaceState = newState; - [self applyPendingInterfaceState]; - } } - (void)applyPendingInterfaceState diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index c511e07a3..56b7e9829 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -676,7 +676,6 @@ - (void)processQueue for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) { __unsafe_unretained id value = *iterator; [value prepareForCATransactionCommit]; - // _queueConsumer(value, isQueueDrained && iterator == itemsEnd - 1); as_log_verbose(ASDisplayLog(), "processed %@", value); } if (count > 1) { diff --git a/Tests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm index 06c6fcd89..196f1e19b 100644 --- a/Tests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -23,6 +23,8 @@ #import #import #import +#import +#import "ASDisplayNodeTestsHelper.h" @interface ASTextCellNodeWithSetSelectedCounter : ASTextCellNode @@ -860,6 +862,7 @@ - (void)testThatDeletedItemsAreMarkedInvisible [cn waitUntilAllUpdatesAreProcessed]; [cn.view layoutIfNeeded]; ASCellNode *node = [cn nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASCATransactionQueueWait(); XCTAssertTrue(node.visible); testController.asyncDelegate->_itemCounts = {0}; [cn deleteItemsAtIndexPaths: @[[NSIndexPath indexPathForItem:0 inSection:0]]]; @@ -1071,6 +1074,7 @@ - (void)testInitialRangeBounds for (NSInteger i = 0; i < c; i++) { NSIndexPath *ip = [NSIndexPath indexPathForItem:i inSection:s]; ASCellNode *node = [cn nodeForItemAtIndexPath:ip]; + ASCATransactionQueueWait(); if (node.inPreloadState) { CGRect frame = [cn.view layoutAttributesForItemAtIndexPath:ip].frame; r = CGRectUnion(r, frame); diff --git a/Tests/ASDisplayNodeImplicitHierarchyTests.m b/Tests/ASDisplayNodeImplicitHierarchyTests.m index 18cb8bf8b..d033ac4f6 100644 --- a/Tests/ASDisplayNodeImplicitHierarchyTests.m +++ b/Tests/ASDisplayNodeImplicitHierarchyTests.m @@ -114,6 +114,7 @@ - (void)testInitialNodeInsertionWhenEnterPreloadState } ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init]; + [node setHierarchyState:ASHierarchyStateRangeManaged]; node.automaticallyManagesSubnodes = YES; node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize) { ASAbsoluteLayoutSpec *absoluteLayout = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[subnodes[3]]]; From e99abf7defdca67c960adc4d8f92664b4e6c5643 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Thu, 1 Feb 2018 16:32:52 -0800 Subject: [PATCH 04/22] Support for disabling ASCATransactionQueue --- Source/ASDisplayNode.mm | 45 +++++++++++++------ Source/ASRunLoopQueue.h | 7 ++- Source/ASRunLoopQueue.mm | 16 +++++++ .../contents.xcworkspacedata | 10 +++++ 4 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 examples/CatDealsCollectionView/Sample.xcworkspace/contents.xcworkspacedata diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 83f1c7fef..bf711506d 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2798,22 +2798,31 @@ - (void)didExitHierarchy // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { - dispatch_async(dispatch_get_main_queue(), ^{ + if ([[ASCATransactionQueue sharedQueue] disabled]) { // This block intentionally retains self. - __instanceLock__.lock(); - unsigned isInHierarchy = _flags.isInHierarchy; - BOOL isVisible = ASInterfaceStateIncludesVisible(_interfaceState); - ASInterfaceState newState = (_interfaceState & ~ASInterfaceStateVisible); - __instanceLock__.unlock(); - - if (!isInHierarchy && isVisible) { - self.interfaceState = newState; - } - }); + dispatch_async(dispatch_get_main_queue(), ^{ + // This block intentionally retains self. + [self _exitVisibleState]; + }); + } else { + [self _exitVisibleState]; + } } } } +- (void)_exitVisibleState { + __instanceLock__.lock(); + unsigned isInHierarchy = _flags.isInHierarchy; + BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); + ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); + __instanceLock__.unlock(); + + if (!isInHierarchy && isVisible) { + self.interfaceState = newState; + } +} + #pragma mark - Interface State /** @@ -2872,13 +2881,18 @@ - (ASInterfaceState)interfaceState - (void)setInterfaceState:(ASInterfaceState)newState { ASDN::MutexLocker l(__instanceLock__); + if ([[ASCATransactionQueue sharedQueue] disabled]) { + [self applyPendingInterfaceState:newState]; + } else { + ASDN::MutexLocker l(__instanceLock__); if (_pendingInterfaceState != newState) { _pendingInterfaceState = newState; [[ASCATransactionQueue sharedQueue] enqueue:self]; } + } } -- (void)applyPendingInterfaceState +- (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState { //This method is currently called on the main thread. The assert has been added here because all of the //did(Enter|Exit)(Display|Visible|Preload)State methods currently guarantee calling on main. @@ -2891,6 +2905,11 @@ - (void)applyPendingInterfaceState ASInterfaceState newState = ASInterfaceStateNone; { ASDN::MutexLocker l(__instanceLock__); + // newPendingState will not be used when ASCATransactionQueue is enabled + // and use _pendingInterfaceState instead for interfaceState update. + if ([[ASCATransactionQueue sharedQueue] disabled]) { + _pendingInterfaceState = newPendingState; + } oldState = _interfaceState; newState = _pendingInterfaceState; if (newState == oldState) { @@ -3000,7 +3019,7 @@ - (void)applyPendingInterfaceState - (void)prepareForCATransactionCommit { - [self applyPendingInterfaceState]; + [self applyPendingInterfaceState:ASInterfaceStateNone]; } - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState diff --git a/Source/ASRunLoopQueue.h b/Source/ASRunLoopQueue.h index 3ac5d7bc8..87c614db5 100644 --- a/Source/ASRunLoopQueue.h +++ b/Source/ASRunLoopQueue.h @@ -56,7 +56,7 @@ AS_SUBCLASSING_RESTRICTED @interface ASCATransactionQueue : NSObject @property (nonatomic, readonly) BOOL isEmpty; - +@property (nonatomic, readonly) BOOL disabled; /** * The queue to run on main run loop before CATransaction commit. * @@ -68,6 +68,11 @@ AS_SUBCLASSING_RESTRICTED - (void)enqueue:(id)object; +/** + * @abstract Apply a node's interfaceState immediately rather than adding to the queue. + */ +- (void)disableInterfaceStateCoalesce; + @end diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 56b7e9829..389eb7215 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -478,6 +478,7 @@ @interface ASCATransactionQueue () { CFRunLoopObserverRef _runLoopObserver; NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance. ASDN::RecursiveMutex _internalQueueLock; + BOOL _disableInterfaceStateCoalesce; // In order to not pollute the top-level activities, each queue has 1 root activity. os_activity_t _rootActivity; @@ -698,6 +699,11 @@ - (void)enqueue:(id)object return; } + if (_disableInterfaceStateCoalesce == YES) { + [object prepareForCATransactionCommit]; + return; + } + ASDN::MutexLocker l(_internalQueueLock); // Check if the object exists. @@ -724,4 +730,14 @@ - (BOOL)isEmpty return _internalQueue.count == 0; } +- (void)disableInterfaceStateCoalesce +{ + _disableInterfaceStateCoalesce = YES; +} + +- (BOOL)disabled +{ + return _disableInterfaceStateCoalesce; +} + @end diff --git a/examples/CatDealsCollectionView/Sample.xcworkspace/contents.xcworkspacedata b/examples/CatDealsCollectionView/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..7b5a2f305 --- /dev/null +++ b/examples/CatDealsCollectionView/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + From 6f2ba8e5fb878e9d7952462d6707dd4401bb9193 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Fri, 2 Feb 2018 11:18:08 -0800 Subject: [PATCH 05/22] Fix didExitHierarchy to use ASCATransactionQueue. --- Source/ASDisplayNode.mm | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index bf711506d..76ad09182 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2788,6 +2788,9 @@ - (void)didExitHierarchy ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); if (![self supportsRangeManagedInterfaceState]) { + // TODO: Consider merging this codepath with the below, so both range-managed and standalone + // nodes wait before firing their exit-visibility handlers. This is more important than it used + // to be, as UIViewController transitions now do rehosting at both start & end of animation. self.interfaceState = ASInterfaceStateNone; } else { // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for @@ -2798,31 +2801,28 @@ - (void)didExitHierarchy // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { - if ([[ASCATransactionQueue sharedQueue] disabled]) { + void(^exitVisibleInterfaceState)(void) = ^{ // This block intentionally retains self. - dispatch_async(dispatch_get_main_queue(), ^{ - // This block intentionally retains self. - [self _exitVisibleState]; - }); + __instanceLock__.lock(); + unsigned isStillInHierarchy = _flags.isInHierarchy; + BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); + ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); + __instanceLock__.unlock(); + + if (!isStillInHierarchy && isVisible) { + self.interfaceState = newState; + }; + }; + + if ([[ASCATransactionQueue sharedQueue] disabled]) { + dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); } else { - [self _exitVisibleState]; + exitVisibleInterfaceState(); } } } } -- (void)_exitVisibleState { - __instanceLock__.lock(); - unsigned isInHierarchy = _flags.isInHierarchy; - BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); - ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); - __instanceLock__.unlock(); - - if (!isInHierarchy && isVisible) { - self.interfaceState = newState; - } -} - #pragma mark - Interface State /** From f807efaa65ed5dbdb6622d06da542e01a53715fa Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 4 Feb 2018 16:28:26 -0800 Subject: [PATCH 06/22] merge range managed and none range managed for didExitHierarchy --- Source/ASDisplayNode.mm | 56 ++- .../contents.xcworkspacedata | 10 + .../InterfaceCoalesceTest/Default-568h@2x.png | Bin 0 -> 17520 bytes .../InterfaceCoalesceTest/Default-667h@2x.png | Bin 0 -> 18314 bytes .../InterfaceCoalesceTest/Default-736h@3x.png | Bin 0 -> 23380 bytes examples_extra/InterfaceCoalesceTest/Podfile | 6 + .../Sample.xcodeproj/project.pbxproj | 385 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Sample.xcscheme | 91 +++++ .../contents.xcworkspacedata | 10 + .../Sample/AppDelegate.h | 18 + .../Sample/AppDelegate.m | 39 ++ .../Sample/CollectionViewController.h | 17 + .../Sample/CollectionViewController.m | 67 +++ .../InterfaceCoalesceTest/Sample/Info.plist | 36 ++ .../Sample/TabBarController.h | 16 + .../Sample/TabBarController.m | 19 + .../Sample/TextCellNode.h | 17 + .../Sample/TextCellNode.m | 100 +++++ .../Sample/ViewController.h | 16 + .../Sample/ViewController.m | 47 +++ .../InterfaceCoalesceTest/Sample/main.m | 20 + 22 files changed, 947 insertions(+), 30 deletions(-) create mode 100644 examples/ASViewController/Sample.xcworkspace/contents.xcworkspacedata create mode 100644 examples_extra/InterfaceCoalesceTest/Default-568h@2x.png create mode 100644 examples_extra/InterfaceCoalesceTest/Default-667h@2x.png create mode 100644 examples_extra/InterfaceCoalesceTest/Default-736h@3x.png create mode 100644 examples_extra/InterfaceCoalesceTest/Podfile create mode 100644 examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.pbxproj create mode 100644 examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme create mode 100644 examples_extra/InterfaceCoalesceTest/Sample.xcworkspace/contents.xcworkspacedata create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.h create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.m create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.h create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.m create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/Info.plist create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/TabBarController.h create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/TabBarController.m create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.h create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.m create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/ViewController.h create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/ViewController.m create mode 100644 examples_extra/InterfaceCoalesceTest/Sample/main.m diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 76ad09182..472274c35 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2787,38 +2787,34 @@ - (void)didExitHierarchy ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - if (![self supportsRangeManagedInterfaceState]) { - // TODO: Consider merging this codepath with the below, so both range-managed and standalone - // nodes wait before firing their exit-visibility handlers. This is more important than it used - // to be, as UIViewController transitions now do rehosting at both start & end of animation. - self.interfaceState = ASInterfaceStateNone; - } else { - // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for - // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. - // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the - // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). - // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer - // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. - - if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { - void(^exitVisibleInterfaceState)(void) = ^{ - // This block intentionally retains self. - __instanceLock__.lock(); - unsigned isStillInHierarchy = _flags.isInHierarchy; - BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); - ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); - __instanceLock__.unlock(); - - if (!isStillInHierarchy && isVisible) { - self.interfaceState = newState; - }; - }; + // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for + // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. + // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the + // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). + // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer + // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. + + if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { + void(^exitVisibleInterfaceState)(void) = ^{ + // This block intentionally retains self. + __instanceLock__.lock(); + unsigned isStillInHierarchy = _flags.isInHierarchy; + BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); + ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); + __instanceLock__.unlock(); - if ([[ASCATransactionQueue sharedQueue] disabled]) { - dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); - } else { - exitVisibleInterfaceState(); + if (!isStillInHierarchy && isVisible) { + if (![self supportsRangeManagedInterfaceState]) { + newState = ASInterfaceStateNone; + } + self.interfaceState = newState; } + }; + + if ([[ASCATransactionQueue sharedQueue] disabled]) { + dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); + } else { + exitVisibleInterfaceState(); } } } diff --git a/examples/ASViewController/Sample.xcworkspace/contents.xcworkspacedata b/examples/ASViewController/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..7b5a2f305 --- /dev/null +++ b/examples/ASViewController/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples_extra/InterfaceCoalesceTest/Default-568h@2x.png b/examples_extra/InterfaceCoalesceTest/Default-568h@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ee80b93937cd9dd79502629b14fe09aa03cacc2 GIT binary patch literal 17520 zcmeI3&2QsG7{;dyhuxq(aR70$vO)rco)4~Hqb-mA2=CIb8^QK*gwP8wCVy+_i!WbB=&euO z!=w19^{!?6gA#W9HYtq<0qu=Ybz>Z0`-H?on{-{TR{Zmu?}~!!)QWeFmfQ*&q~~s* zhveXV_s~8+u`5n-qh6?vEov|zF&4&yz86{JS~2yt=yB346@|1*d{QfJCIbpbtv#XP zheR++hG@%*E|`^)Vkml9c~ekjMU!MrQZ!LfExBSThA{aQ>jipL4V{j)-@H8;jz+a& zFOCCCl18IZX{43>uq!E*N=1@YNmWJKLyXS67>`9Sx|NwseVQb)LpO+B-xCsF-1diY ztyoM3ntdkMH3(({dC`O&r6`SYASoqTS|)PrnI;&9{q)ovTOxfjAYL3%ow8IH^!(V5 zdj5(bXX%v#(>ZCiW@9fs-@#z%&{4c~N)b$uE>%W{X91D+N#qYhn{1uZOS!e|>SMPv zpPUO$NoM7_ld-!(mSi$nx)ib*s?uw<8X>{4A0GOCzn-nKy(vPW(GXs1VcYc*q_0;c z*nd9Rb1TxsF{#tVsEdj$D*B-+TUy1EO;I*2Sj^#R z=5cV0FXfW&oAYsOtK)|Q9M|0e?h+~Rx>af3nCm%PQdYz7`yo9oQrD`|vgVvBU1rvf z7sc4K$xgFQ8%nP0Sc)olh@)wuzTR$&x`VM;Hf>YXY_n{bs#dn!Y6`K{%F7q5o4!3v zw#vlXq1KM0n~q5oQE_zYZ)g>1Jsbid6i*sMS$nsnP**iK4W z-A;A`ajMdV*7<48loOe|IDwa=ocZVEtH&7ih{xJcnN`|rwMpc6;t>wXW|yvs$8Pk@ z@}dTMSEZ!x_uck_9fO)qQO@GMQ+b+k+QN;k1G;mdod%W(l9?2zMP^8s0o3jkq<92c7p$Z}i&2s`As z*nB{i;{rg~A;-n$1F{?!0KyJAE;b*K<+uP4cF1wD`G73P1%R+aj*HC)WH~MXgdK8R zY(5~%aRDIgkmF+W0a=a<0AYt57n={ra$EoiJ7nT2%-^yl9(}cTMBkzP^v2h3(D!cz zdwaiy(D|zfJ@^QrzyG1%zali05&G>uLe}R9z2tv(@5kE+U4MV4xp_GL`Sg5w7{{k8fuN`-gg~6EtdKy$@q3ealPsm_(n_S1wutt$JFzFN)xMF-1^Yl zKS&PRZ`w}KFH<+@u=1!M^4^5hZ;wLioUladup`fJl>Yeo+mhtDjncbTTWyEy?AY5p zkJ#S%_P%p|;?&&I?dEcQWOIW)OQbw>80kV~ynhxlWtYXlAadBoDZiAPi>^NL zy0gi-;FM-AJ$E+pE|H~~T$U|`e1_`$TJ80S(IklWgP_;USJ}=4p|rj(z1*gb=chz*y}FfH5CiynoZ z(1ULtmnQT|F2%kDAJ?(FLDZ*7)9ceCriA`cU70l&dQO*=y&m*}h@Tc~8g*q+b3v6Y zGkeRA6Y4u`tJUNUWzTbMv)ZYeIx}Tvs=93I-HKc_OiS*t8m(1Toz=z=+wG!!&bk#i zgLJEmtzB+S4XSl5!;n{9oyw*`b-6>UrmUK^il*vDw??bk{BY}ne9ro<$m3;>_6mK{ zv;U_`>IE_XGxBbybA%AHk*$y&Essi=Cl+F`4c zIsUhEU>dfjO$yTgGzYWw>l{=6h`CK=a#@px$7$NGR{I`p>s+{xJnqw$@4<_ua8kkN zOJ_ZOc(8fd2=@U+V2j1fk5@J z8HQPu7E)trK3jz+=d5(*t^B#1|0GbRzX|55>h#WYod>gPx=vT%g@XVf;t+9(`G73q z0zkwe;u7-#S;Pf^h(p9B<^!^b3jh&^h)c`|WDyqtA`TIkm=DMzE&xOvA}%o>kVRYo zh&V)CVm=^?xBw7wh`7XjKo)TUAmR{liTQvm;sQX#A>tDA0a?TafQUoHCFTRNhzkG_ zhloqe2V@Z!03r?%mzWR8A}#<#93n0;ACN^{0Ejq5Tw*>Ti?{#~afrCYd_Wd)0U+WK zaf$hWEaCz{#3AAm^8s1J1%QY{#3kkfvWN=+5r;xt%d@v^na^LX9rAZ*rRRS9n7@B3 zIh(s}Le5_zCFIw8gxH@D@_g{o-5>7o_jw0ft+oBp&%b@Yw8WM7 zrH5boo3EvZ_(1|l00|%gB!C2v01`j~NZ=X>eD`35{4^j-PY%DimD+7>Y`4C6{oeh* E06EB-U;qFB literal 0 HcmV?d00001 diff --git a/examples_extra/InterfaceCoalesceTest/Default-736h@3x.png b/examples_extra/InterfaceCoalesceTest/Default-736h@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8949cae16a4217842bd8ad813422c6b02e7cafa GIT binary patch literal 23380 zcmeI3&2QsG7{;dvp`~agBn}`rU}c2_5{^Co*u*CBswBI#5^1Zpi0)~3Y)@Ki6WiGC zChY|TCvFHXE5u>%NL;wV0fEGo^J@PC5E5KCaDuU&4|kFdg zdhMfNZ$I1by=i;VuulBQrS}<*{ybMEgw+Y z?`=z+D4~*BH)T)7hSad?*u+K?zba`e))iG(ur6cGRxKNw(&STfR@qT2@%#2p_u6DQ z7PV`KSr*%hG8&EQBfTCa2MV?4Y7lsEkRh;JT_T6Zzgu6CWjm;?#Ukp#wUkVU{u-UaE@^ zqby1fqcet_rOzCg%}K8}8++;b4u?yJPP41G8G;GYrOI^gIHt-DO{1g4qgQXUOS!b{ z>a(CfpPW-pdFIS>r{mxZS)M6n#Zo9|sKu_;?j)3CQL-0B1E*YN+f#&6rz5@GBVG{Z zNMC6weE<1m&#h>eWYl4c(U7q!V`EQKZH#RestsFJD<)-6&Z8IkLH~G(hhf@Aqv}!V z$$PNP%ca`4;^TXEKT3uqbAll`ph_Gbw3K;crRQu(*_~(*CG51Qqqmf0%@tL# z%OtV!#5ekuMW}3fo+cYjqbZZ7=E~GCvFmv%we)@gvDd507p%LH zca(3HiM7wHU9)NG4c*OMb=lB-OLlervW%Ms#tqVRUEP{mSL6%UTS>sm92r#lFw}aGvc-8^S+s2F7KLn=zH_>DnivE{L5fL|(tNwMYt#KUt6;MNm1~M^YZEUo zWsaBc2I{wzQ?2vUnkgr;U~vM^N4fN`$j=^QbVx(dhAOR!UT2%6Q9m1zgsvU1HSw1l zy|g^7;k{c*UiSyVzc33ax&2^sKn+c6P*;_G^K!n@JzsX48kQ}r_EkzOqv5;LIsT_} zU}&~ED@gy*9L(3RcSynm>O0ExvZf7>(zKng_C46vIdva-)Tgc7gQrX3w1O{|&Q|{L zV6(EzN&qR!9d0QLZSw_F_TSIT=isR5-_TU{QE>i$BCV!*>25)XkQ{H}i_^U`z-5-GJRH)BFa2HG_>+sQA=U>Gio()6`~F zT1ic$<#bgZor~I8wz3Cv_M1SN{U}%{tFv3r!#tQ@)5CP-ykHOxh&TjXVm@3JaB)Dy zA>b18;j(~>10oIqmzWQi1za2uaR|7?e7G#&;(&-lz$NCxWdRolL>vMxF&{1qxHur< z5O9h4a9O~`0TG9QOU#GM0xk}SI0Rf`K3o=XaX`c&;1cuUvVe;NA`StUm=Bi)TpSQ_ z2)M+2xGdn}fQUoDCFa9r0T%~E90D#eA1({HI3VH>aEbYFS-`~s5r=?F%!kVYE)Iw| z1YBZ1To!O~K*S;767%7*fQthn4gr^#50?d891w9R#I-tq&6bAj-P#d*iT2)B=M(k< zuH>!n^bk6E38D8sKf00e*l5C8%|00;m9AOHk_01yBIKmZ5;0U!VbfB+Bx0zd!= l00AHX1c1Q*gn;t`y7MJkdHVCOto({Mu5Na}c>U)4e*&NtopJyG literal 0 HcmV?d00001 diff --git a/examples_extra/InterfaceCoalesceTest/Podfile b/examples_extra/InterfaceCoalesceTest/Podfile new file mode 100644 index 000000000..667002269 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Podfile @@ -0,0 +1,6 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +target 'Sample' do + pod 'Texture/Yoga', :path => '../..' +end + diff --git a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.pbxproj b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.pbxproj new file mode 100644 index 000000000..86a3f35f7 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.pbxproj @@ -0,0 +1,385 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */; }; + 05E2128719D4DB510098F589 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128619D4DB510098F589 /* main.m */; }; + 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128919D4DB510098F589 /* AppDelegate.m */; }; + 05E2128D19D4DB510098F589 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128C19D4DB510098F589 /* ViewController.m */; }; + 6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AA19EE274300767484 /* Default-667h@2x.png */; }; + 6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AB19EE274300767484 /* Default-736h@3x.png */; }; + 92F1263CECFE3FFCC7A5F936 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E8EC8300ABAAEA079224272A /* libPods-Sample.a */; }; + C081EE8D1F85AEEC00F0B5F1 /* TabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = C081EE8C1F85AEEC00F0B5F1 /* TabBarController.m */; }; + C081EE921F85AFB800F0B5F1 /* CollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C081EE8F1F85AFB800F0B5F1 /* CollectionViewController.m */; }; + C081EE931F85AFB800F0B5F1 /* TextCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = C081EE911F85AFB800F0B5F1 /* TextCellNode.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; + 05E2128119D4DB510098F589 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 05E2128519D4DB510098F589 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 05E2128619D4DB510098F589 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 05E2128819D4DB510098F589 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 05E2128919D4DB510098F589 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 05E2128B19D4DB510098F589 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 05E2128C19D4DB510098F589 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 0CDEE995962D3E4584D302EE /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = ""; }; + 6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; }; + 6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; }; + A950870A2154F92D5DC91F1A /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = ""; }; + C081EE8B1F85AEEC00F0B5F1 /* TabBarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TabBarController.h; sourceTree = ""; }; + C081EE8C1F85AEEC00F0B5F1 /* TabBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TabBarController.m; sourceTree = ""; }; + C081EE8E1F85AFB800F0B5F1 /* CollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionViewController.h; sourceTree = ""; }; + C081EE8F1F85AFB800F0B5F1 /* CollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionViewController.m; sourceTree = ""; }; + C081EE901F85AFB800F0B5F1 /* TextCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCellNode.h; sourceTree = ""; }; + C081EE911F85AFB800F0B5F1 /* TextCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextCellNode.m; sourceTree = ""; }; + E8EC8300ABAAEA079224272A /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 05E2127E19D4DB510098F589 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 92F1263CECFE3FFCC7A5F936 /* libPods-Sample.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 05E2127819D4DB510098F589 = { + isa = PBXGroup; + children = ( + 05E2128319D4DB510098F589 /* Sample */, + 05E2128219D4DB510098F589 /* Products */, + 6DE0E5D094594AB09140EF84 /* Pods */, + F5DF5EAD6C1B97F91D1C830F /* Frameworks */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 05E2128219D4DB510098F589 /* Products */ = { + isa = PBXGroup; + children = ( + 05E2128119D4DB510098F589 /* Sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 05E2128319D4DB510098F589 /* Sample */ = { + isa = PBXGroup; + children = ( + C081EE8E1F85AFB800F0B5F1 /* CollectionViewController.h */, + C081EE8F1F85AFB800F0B5F1 /* CollectionViewController.m */, + C081EE901F85AFB800F0B5F1 /* TextCellNode.h */, + C081EE911F85AFB800F0B5F1 /* TextCellNode.m */, + 05E2128819D4DB510098F589 /* AppDelegate.h */, + 05E2128919D4DB510098F589 /* AppDelegate.m */, + 05E2128B19D4DB510098F589 /* ViewController.h */, + 05E2128C19D4DB510098F589 /* ViewController.m */, + 05E2128419D4DB510098F589 /* Supporting Files */, + C081EE8B1F85AEEC00F0B5F1 /* TabBarController.h */, + C081EE8C1F85AEEC00F0B5F1 /* TabBarController.m */, + ); + path = Sample; + sourceTree = ""; + }; + 05E2128419D4DB510098F589 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */, + 6C2C82AA19EE274300767484 /* Default-667h@2x.png */, + 6C2C82AB19EE274300767484 /* Default-736h@3x.png */, + 05E2128519D4DB510098F589 /* Info.plist */, + 05E2128619D4DB510098F589 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 6DE0E5D094594AB09140EF84 /* Pods */ = { + isa = PBXGroup; + children = ( + 0CDEE995962D3E4584D302EE /* Pods-Sample.debug.xcconfig */, + A950870A2154F92D5DC91F1A /* Pods-Sample.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + F5DF5EAD6C1B97F91D1C830F /* Frameworks */ = { + isa = PBXGroup; + children = ( + E8EC8300ABAAEA079224272A /* libPods-Sample.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 05E2128019D4DB510098F589 /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + 77F6A2B5E8DA12933E6365CE /* [CP] Check Pods Manifest.lock */, + 05E2127D19D4DB510098F589 /* Sources */, + 05E2127E19D4DB510098F589 /* Frameworks */, + 05E2127F19D4DB510098F589 /* Resources */, + 96436DA0C1AFF84D8041B522 /* [CP] Embed Pods Frameworks */, + D17B5BD4AA634EFE93D71E9F /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = Sample; + productReference = 05E2128119D4DB510098F589 /* Sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 05E2127919D4DB510098F589 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 05E2128019D4DB510098F589 = { + CreatedOnToolsVersion = 6.0.1; + }; + }; + }; + buildConfigurationList = 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 05E2127819D4DB510098F589; + productRefGroup = 05E2128219D4DB510098F589 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 05E2128019D4DB510098F589 /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 05E2127F19D4DB510098F589 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */, + 6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */, + 6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 77F6A2B5E8DA12933E6365CE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 96436DA0C1AFF84D8041B522 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + D17B5BD4AA634EFE93D71E9F /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 05E2127D19D4DB510098F589 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C081EE931F85AFB800F0B5F1 /* TextCellNode.m in Sources */, + 05E2128D19D4DB510098F589 /* ViewController.m in Sources */, + C081EE8D1F85AEEC00F0B5F1 /* TabBarController.m in Sources */, + 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */, + C081EE921F85AFB800F0B5F1 /* CollectionViewController.m in Sources */, + 05E2128719D4DB510098F589 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 05E212A219D4DB510098F589 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 05E212A319D4DB510098F589 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 05E212A519D4DB510098F589 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0CDEE995962D3E4584D302EE /* Pods-Sample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 05E212A619D4DB510098F589 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A950870A2154F92D5DC91F1A /* Pods-Sample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05E212A219D4DB510098F589 /* Debug */, + 05E212A319D4DB510098F589 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05E212A519D4DB510098F589 /* Debug */, + 05E212A619D4DB510098F589 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 05E2127919D4DB510098F589 /* Project object */; +} diff --git a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme new file mode 100644 index 000000000..d41d58c5d --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples_extra/InterfaceCoalesceTest/Sample.xcworkspace/contents.xcworkspacedata b/examples_extra/InterfaceCoalesceTest/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..7b5a2f305 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.h b/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.h new file mode 100644 index 000000000..2aa29369b --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.h @@ -0,0 +1,18 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.m b/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.m new file mode 100644 index 000000000..7b8d54bd3 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.m @@ -0,0 +1,39 @@ +// +// AppDelegate.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "AppDelegate.h" + +#import "CollectionViewController.h" +#import "ViewController.h" + + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.backgroundColor = [UIColor whiteColor]; + + ViewController *viewController = [[ViewController alloc] init]; +// viewController.tabBarItem.title = @"TextStress"; + + + +// TabBarController *tabBarController = [[TabBarController alloc] init]; +// tabBarController.viewControllers = @[cvc, viewController]; + UINavigationController *nvc = [[UINavigationController alloc] initWithRootViewController:viewController]; + self.window.rootViewController = nvc; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.h b/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.h new file mode 100644 index 000000000..159b2fa1c --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.h @@ -0,0 +1,17 @@ +// +// CollectionViewController.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@interface CollectionViewController : ASViewController +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.m b/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.m new file mode 100644 index 000000000..d93005236 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.m @@ -0,0 +1,67 @@ +// +// CollectionViewController.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "CollectionViewController.h" +#import "TextCellNode.h" + +@interface CollectionViewController() +{ + ASCollectionNode *_collectionNode; + NSArray *_labels; + TextCellNode *_cellNode; +} + +@end + +@implementation CollectionViewController + +- (instancetype)init +{ + UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; + _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:flowLayout]; + CGRect rect = [[UIApplication sharedApplication] statusBarFrame]; + _collectionNode.contentInset = UIEdgeInsetsMake(rect.size.height, 0, 0, 0); + self = [super initWithNode:_collectionNode]; + if (self) { + _collectionNode.delegate = self; + _collectionNode.dataSource = self; + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + _collectionNode.backgroundColor = [UIColor whiteColor]; + _labels = @[@"Fight of the Living Dead: Experiment Fight of the Living Dead: Experiment", @"S1 • E1"]; +} + +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section +{ + return 1; +} + +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return ^{ + _cellNode = [[TextCellNode alloc] initWithText1:_labels[0] text2:_labels[1]]; + return _cellNode; + }; +} + +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + CGFloat width = collectionNode.view.bounds.size.width; + return ASSizeRangeMake(CGSizeMake(width, 0.0f), CGSizeMake(width, CGFLOAT_MAX)); +} + +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/Info.plist b/examples_extra/InterfaceCoalesceTest/Sample/Info.plist new file mode 100644 index 000000000..fb4115c84 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.h b/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.h new file mode 100644 index 000000000..5a25bb04a --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.h @@ -0,0 +1,16 @@ +// +// TabBarController.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface TabBarController : ASTabBarController +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.m b/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.m new file mode 100644 index 000000000..a7905cb23 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.m @@ -0,0 +1,19 @@ +// +// TabBarController.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "TabBarController.h" + +@interface TabBarController () +@end + +@implementation TabBarController +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.h b/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.h new file mode 100644 index 000000000..c4b35cfaf --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.h @@ -0,0 +1,17 @@ +// +// TextCellNode.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface TextCellNode : ASCellNode +- (instancetype)initWithText1:(NSString *)text1 text2:(NSString *)text2; +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.m b/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.m new file mode 100644 index 000000000..13bb7694c --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.m @@ -0,0 +1,100 @@ +// +// TextCellNode.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "TextCellNode.h" +#import +#import + +#ifndef USE_ASTEXTNODE_2 +#define USE_ASTEXTNODE_2 1 +#endif + +@interface TextCellNode() +{ +#if USE_ASTEXTNODE_2 + ASTextNode2 *_label1; + ASTextNode2 *_label2; +#else + ASTextNode *_label1; + ASTextNode *_label2; +#endif +} +@end + +@implementation TextCellNode + +- (instancetype)initWithText1:(NSString *)text1 text2:(NSString *)text2 +{ + self = [super init]; + if (self) { + self.automaticallyManagesSubnodes = YES; + self.clipsToBounds = YES; +#if USE_ASTEXTNODE_2 + _label1 = [[ASTextNode2 alloc] init]; + _label2 = [[ASTextNode2 alloc] init]; +#else + _label1 = [[ASTextNode alloc] init]; + _label2 = [[ASTextNode alloc] init]; +#endif + + _label1.attributedText = [[NSAttributedString alloc] initWithString:text1]; + _label2.attributedText = [[NSAttributedString alloc] initWithString:text2]; + + _label1.maximumNumberOfLines = 1; + _label1.truncationMode = NSLineBreakByTruncatingTail; + _label2.maximumNumberOfLines = 1; + _label2.truncationMode = NSLineBreakByTruncatingTail; + + [self simpleSetupYogaLayout]; + } + return self; +} + +/** + This is to text a row with two labels, the first should be truncated with "...". + Layout is like: [l1Container[_label1], label2]. + This shows a bug of ASTextNode2. + */ +- (void)simpleSetupYogaLayout +{ + [self.style yogaNodeCreateIfNeeded]; + [_label1.style yogaNodeCreateIfNeeded]; + [_label2.style yogaNodeCreateIfNeeded]; + + _label1.style.flexGrow = 0; + _label1.style.flexShrink = 1; + _label1.backgroundColor = [UIColor lightGrayColor]; + + _label2.style.flexGrow = 0; + _label2.style.flexShrink = 0; + _label2.backgroundColor = [UIColor greenColor]; + + ASDisplayNode *l1Container = [ASDisplayNode yogaVerticalStack]; + + // TODO(fix ASTextNode2): next two line will show the bug of TextNode2 + // which works for ASTextNode though + // see discussion here: https://github.com/TextureGroup/Texture/pull/553 + l1Container.style.alignItems = ASStackLayoutAlignItemsCenter; + _label1.style.alignSelf = ASStackLayoutAlignSelfStart; + + l1Container.style.flexGrow = 0; + l1Container.style.flexShrink = 1; + + l1Container.yogaChildren = @[_label1]; + + self.style.justifyContent = ASStackLayoutJustifyContentSpaceBetween; + self.style.alignItems = ASStackLayoutAlignItemsStart; + self.style.flexDirection = ASStackLayoutDirectionHorizontal; + self.yogaChildren = @[l1Container, _label2]; +} + +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/ViewController.h b/examples_extra/InterfaceCoalesceTest/Sample/ViewController.h new file mode 100644 index 000000000..d0e9200d8 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/ViewController.h @@ -0,0 +1,16 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +@interface ViewController : UIViewController + +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/ViewController.m b/examples_extra/InterfaceCoalesceTest/Sample/ViewController.m new file mode 100644 index 000000000..8968fadd1 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/ViewController.m @@ -0,0 +1,47 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "ViewController.h" +#import + +#define NUMBER_ELEMENTS 2 + +@interface ViewController () +{ + ASButtonNode *_node; +} + +@end + + +@implementation ViewController + ++(void)initialize { + [[ASCATransactionQueue sharedQueue] disableInterfaceStateCoalesce]; +} +- (void)viewDidLoad +{ + [super viewDidLoad]; + + _node = [[ASButtonNode alloc] init]; + _node.frame = CGRectMake(0, 100, 100, 100); + _node.backgroundColor = [UIColor greenColor]; + + [self.view addSubnode:_node]; + [_node addTarget:self action:@selector(clicked:) forControlEvents:ASControlNodeEventTouchUpInside]; +} + +- (void)clicked:(id)sender { + ViewController *vc = [[ViewController alloc] init]; + [self.navigationController pushViewController:vc animated:YES]; +} + +@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/main.m b/examples_extra/InterfaceCoalesceTest/Sample/main.m new file mode 100644 index 000000000..ae9488711 --- /dev/null +++ b/examples_extra/InterfaceCoalesceTest/Sample/main.m @@ -0,0 +1,20 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} From bd8cf01a82f90dc17ad20323740de3ae88dc9dff Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 4 Feb 2018 16:29:59 -0800 Subject: [PATCH 07/22] Revert "merge range managed and none range managed for didExitHierarchy" This reverts commit f807efaa65ed5dbdb6622d06da542e01a53715fa. --- Source/ASDisplayNode.mm | 56 +-- .../contents.xcworkspacedata | 10 - .../InterfaceCoalesceTest/Default-568h@2x.png | Bin 17520 -> 0 bytes .../InterfaceCoalesceTest/Default-667h@2x.png | Bin 18314 -> 0 bytes .../InterfaceCoalesceTest/Default-736h@3x.png | Bin 23380 -> 0 bytes examples_extra/InterfaceCoalesceTest/Podfile | 6 - .../Sample.xcodeproj/project.pbxproj | 385 ------------------ .../contents.xcworkspacedata | 7 - .../xcshareddata/xcschemes/Sample.xcscheme | 91 ----- .../contents.xcworkspacedata | 10 - .../Sample/AppDelegate.h | 18 - .../Sample/AppDelegate.m | 39 -- .../Sample/CollectionViewController.h | 17 - .../Sample/CollectionViewController.m | 67 --- .../InterfaceCoalesceTest/Sample/Info.plist | 36 -- .../Sample/TabBarController.h | 16 - .../Sample/TabBarController.m | 19 - .../Sample/TextCellNode.h | 17 - .../Sample/TextCellNode.m | 100 ----- .../Sample/ViewController.h | 16 - .../Sample/ViewController.m | 47 --- .../InterfaceCoalesceTest/Sample/main.m | 20 - 22 files changed, 30 insertions(+), 947 deletions(-) delete mode 100644 examples/ASViewController/Sample.xcworkspace/contents.xcworkspacedata delete mode 100644 examples_extra/InterfaceCoalesceTest/Default-568h@2x.png delete mode 100644 examples_extra/InterfaceCoalesceTest/Default-667h@2x.png delete mode 100644 examples_extra/InterfaceCoalesceTest/Default-736h@3x.png delete mode 100644 examples_extra/InterfaceCoalesceTest/Podfile delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.pbxproj delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample.xcworkspace/contents.xcworkspacedata delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.h delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.m delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.h delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.m delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/Info.plist delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/TabBarController.h delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/TabBarController.m delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.h delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.m delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/ViewController.h delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/ViewController.m delete mode 100644 examples_extra/InterfaceCoalesceTest/Sample/main.m diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 472274c35..76ad09182 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2787,34 +2787,38 @@ - (void)didExitHierarchy ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for - // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. - // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the - // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). - // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer - // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. - - if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { - void(^exitVisibleInterfaceState)(void) = ^{ - // This block intentionally retains self. - __instanceLock__.lock(); - unsigned isStillInHierarchy = _flags.isInHierarchy; - BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); - ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); - __instanceLock__.unlock(); + if (![self supportsRangeManagedInterfaceState]) { + // TODO: Consider merging this codepath with the below, so both range-managed and standalone + // nodes wait before firing their exit-visibility handlers. This is more important than it used + // to be, as UIViewController transitions now do rehosting at both start & end of animation. + self.interfaceState = ASInterfaceStateNone; + } else { + // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for + // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. + // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the + // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). + // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer + // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. + + if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { + void(^exitVisibleInterfaceState)(void) = ^{ + // This block intentionally retains self. + __instanceLock__.lock(); + unsigned isStillInHierarchy = _flags.isInHierarchy; + BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); + ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); + __instanceLock__.unlock(); + + if (!isStillInHierarchy && isVisible) { + self.interfaceState = newState; + }; + }; - if (!isStillInHierarchy && isVisible) { - if (![self supportsRangeManagedInterfaceState]) { - newState = ASInterfaceStateNone; - } - self.interfaceState = newState; + if ([[ASCATransactionQueue sharedQueue] disabled]) { + dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); + } else { + exitVisibleInterfaceState(); } - }; - - if ([[ASCATransactionQueue sharedQueue] disabled]) { - dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); - } else { - exitVisibleInterfaceState(); } } } diff --git a/examples/ASViewController/Sample.xcworkspace/contents.xcworkspacedata b/examples/ASViewController/Sample.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7b5a2f305..000000000 --- a/examples/ASViewController/Sample.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/examples_extra/InterfaceCoalesceTest/Default-568h@2x.png b/examples_extra/InterfaceCoalesceTest/Default-568h@2x.png deleted file mode 100644 index 6ee80b93937cd9dd79502629b14fe09aa03cacc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17520 zcmeI3&2QsG7{;dyhuxq(aR70$vO)rco)4~Hqb-mA2=CIb8^QK*gwP8wCVy+_i!WbB=&euO z!=w19^{!?6gA#W9HYtq<0qu=Ybz>Z0`-H?on{-{TR{Zmu?}~!!)QWeFmfQ*&q~~s* zhveXV_s~8+u`5n-qh6?vEov|zF&4&yz86{JS~2yt=yB346@|1*d{QfJCIbpbtv#XP zheR++hG@%*E|`^)Vkml9c~ekjMU!MrQZ!LfExBSThA{aQ>jipL4V{j)-@H8;jz+a& zFOCCCl18IZX{43>uq!E*N=1@YNmWJKLyXS67>`9Sx|NwseVQb)LpO+B-xCsF-1diY ztyoM3ntdkMH3(({dC`O&r6`SYASoqTS|)PrnI;&9{q)ovTOxfjAYL3%ow8IH^!(V5 zdj5(bXX%v#(>ZCiW@9fs-@#z%&{4c~N)b$uE>%W{X91D+N#qYhn{1uZOS!e|>SMPv zpPUO$NoM7_ld-!(mSi$nx)ib*s?uw<8X>{4A0GOCzn-nKy(vPW(GXs1VcYc*q_0;c z*nd9Rb1TxsF{#tVsEdj$D*B-+TUy1EO;I*2Sj^#R z=5cV0FXfW&oAYsOtK)|Q9M|0e?h+~Rx>af3nCm%PQdYz7`yo9oQrD`|vgVvBU1rvf z7sc4K$xgFQ8%nP0Sc)olh@)wuzTR$&x`VM;Hf>YXY_n{bs#dn!Y6`K{%F7q5o4!3v zw#vlXq1KM0n~q5oQE_zYZ)g>1Jsbid6i*sMS$nsnP**iK4W z-A;A`ajMdV*7<48loOe|IDwa=ocZVEtH&7ih{xJcnN`|rwMpc6;t>wXW|yvs$8Pk@ z@}dTMSEZ!x_uck_9fO)qQO@GMQ+b+k+QN;k1G;mdod%W(l9?2zMP^8s0o3jkq<92c7p$Z}i&2s`As z*nB{i;{rg~A;-n$1F{?!0KyJAE;b*K<+uP4cF1wD`G73P1%R+aj*HC)WH~MXgdK8R zY(5~%aRDIgkmF+W0a=a<0AYt57n={ra$EoiJ7nT2%-^yl9(}cTMBkzP^v2h3(D!cz zdwaiy(D|zfJ@^QrzyG1%zali05&G>uLe}R9z2tv(@5kE+U4MV4xp_GL`Sg5w7{{k8fuN`-gg~6EtdKy$@q3ealPsm_(n_S1wutt$JFzFN)xMF-1^Yl zKS&PRZ`w}KFH<+@u=1!M^4^5hZ;wLioUladup`fJl>Yeo+mhtDjncbTTWyEy?AY5p zkJ#S%_P%p|;?&&I?dEcQWOIW)OQbw>80kV~ynhxlWtYXlAadBoDZiAPi>^NL zy0gi-;FM-AJ$E+pE|H~~T$U|`e1_`$TJ80S(IklWgP_;USJ}=4p|rj(z1*gb=chz*y}FfH5CiynoZ z(1ULtmnQT|F2%kDAJ?(FLDZ*7)9ceCriA`cU70l&dQO*=y&m*}h@Tc~8g*q+b3v6Y zGkeRA6Y4u`tJUNUWzTbMv)ZYeIx}Tvs=93I-HKc_OiS*t8m(1Toz=z=+wG!!&bk#i zgLJEmtzB+S4XSl5!;n{9oyw*`b-6>UrmUK^il*vDw??bk{BY}ne9ro<$m3;>_6mK{ zv;U_`>IE_XGxBbybA%AHk*$y&Essi=Cl+F`4c zIsUhEU>dfjO$yTgGzYWw>l{=6h`CK=a#@px$7$NGR{I`p>s+{xJnqw$@4<_ua8kkN zOJ_ZOc(8fd2=@U+V2j1fk5@J z8HQPu7E)trK3jz+=d5(*t^B#1|0GbRzX|55>h#WYod>gPx=vT%g@XVf;t+9(`G73q z0zkwe;u7-#S;Pf^h(p9B<^!^b3jh&^h)c`|WDyqtA`TIkm=DMzE&xOvA}%o>kVRYo zh&V)CVm=^?xBw7wh`7XjKo)TUAmR{liTQvm;sQX#A>tDA0a?TafQUoHCFTRNhzkG_ zhloqe2V@Z!03r?%mzWR8A}#<#93n0;ACN^{0Ejq5Tw*>Ti?{#~afrCYd_Wd)0U+WK zaf$hWEaCz{#3AAm^8s1J1%QY{#3kkfvWN=+5r;xt%d@v^na^LX9rAZ*rRRS9n7@B3 zIh(s}Le5_zCFIw8gxH@D@_g{o-5>7o_jw0ft+oBp&%b@Yw8WM7 zrH5boo3EvZ_(1|l00|%gB!C2v01`j~NZ=X>eD`35{4^j-PY%DimD+7>Y`4C6{oeh* E06EB-U;qFB diff --git a/examples_extra/InterfaceCoalesceTest/Default-736h@3x.png b/examples_extra/InterfaceCoalesceTest/Default-736h@3x.png deleted file mode 100644 index c8949cae16a4217842bd8ad813422c6b02e7cafa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23380 zcmeI3&2QsG7{;dvp`~agBn}`rU}c2_5{^Co*u*CBswBI#5^1Zpi0)~3Y)@Ki6WiGC zChY|TCvFHXE5u>%NL;wV0fEGo^J@PC5E5KCaDuU&4|kFdg zdhMfNZ$I1by=i;VuulBQrS}<*{ybMEgw+Y z?`=z+D4~*BH)T)7hSad?*u+K?zba`e))iG(ur6cGRxKNw(&STfR@qT2@%#2p_u6DQ z7PV`KSr*%hG8&EQBfTCa2MV?4Y7lsEkRh;JT_T6Zzgu6CWjm;?#Ukp#wUkVU{u-UaE@^ zqby1fqcet_rOzCg%}K8}8++;b4u?yJPP41G8G;GYrOI^gIHt-DO{1g4qgQXUOS!b{ z>a(CfpPW-pdFIS>r{mxZS)M6n#Zo9|sKu_;?j)3CQL-0B1E*YN+f#&6rz5@GBVG{Z zNMC6weE<1m&#h>eWYl4c(U7q!V`EQKZH#RestsFJD<)-6&Z8IkLH~G(hhf@Aqv}!V z$$PNP%ca`4;^TXEKT3uqbAll`ph_Gbw3K;crRQu(*_~(*CG51Qqqmf0%@tL# z%OtV!#5ekuMW}3fo+cYjqbZZ7=E~GCvFmv%we)@gvDd507p%LH zca(3HiM7wHU9)NG4c*OMb=lB-OLlervW%Ms#tqVRUEP{mSL6%UTS>sm92r#lFw}aGvc-8^S+s2F7KLn=zH_>DnivE{L5fL|(tNwMYt#KUt6;MNm1~M^YZEUo zWsaBc2I{wzQ?2vUnkgr;U~vM^N4fN`$j=^QbVx(dhAOR!UT2%6Q9m1zgsvU1HSw1l zy|g^7;k{c*UiSyVzc33ax&2^sKn+c6P*;_G^K!n@JzsX48kQ}r_EkzOqv5;LIsT_} zU}&~ED@gy*9L(3RcSynm>O0ExvZf7>(zKng_C46vIdva-)Tgc7gQrX3w1O{|&Q|{L zV6(EzN&qR!9d0QLZSw_F_TSIT=isR5-_TU{QE>i$BCV!*>25)XkQ{H}i_^U`z-5-GJRH)BFa2HG_>+sQA=U>Gio()6`~F zT1ic$<#bgZor~I8wz3Cv_M1SN{U}%{tFv3r!#tQ@)5CP-ykHOxh&TjXVm@3JaB)Dy zA>b18;j(~>10oIqmzWQi1za2uaR|7?e7G#&;(&-lz$NCxWdRolL>vMxF&{1qxHur< z5O9h4a9O~`0TG9QOU#GM0xk}SI0Rf`K3o=XaX`c&;1cuUvVe;NA`StUm=Bi)TpSQ_ z2)M+2xGdn}fQUoDCFa9r0T%~E90D#eA1({HI3VH>aEbYFS-`~s5r=?F%!kVYE)Iw| z1YBZ1To!O~K*S;767%7*fQthn4gr^#50?d891w9R#I-tq&6bAj-P#d*iT2)B=M(k< zuH>!n^bk6E38D8sKf00e*l5C8%|00;m9AOHk_01yBIKmZ5;0U!VbfB+Bx0zd!= l00AHX1c1Q*gn;t`y7MJkdHVCOto({Mu5Na}c>U)4e*&NtopJyG diff --git a/examples_extra/InterfaceCoalesceTest/Podfile b/examples_extra/InterfaceCoalesceTest/Podfile deleted file mode 100644 index 667002269..000000000 --- a/examples_extra/InterfaceCoalesceTest/Podfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' -target 'Sample' do - pod 'Texture/Yoga', :path => '../..' -end - diff --git a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.pbxproj b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.pbxproj deleted file mode 100644 index 86a3f35f7..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.pbxproj +++ /dev/null @@ -1,385 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */; }; - 05E2128719D4DB510098F589 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128619D4DB510098F589 /* main.m */; }; - 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128919D4DB510098F589 /* AppDelegate.m */; }; - 05E2128D19D4DB510098F589 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128C19D4DB510098F589 /* ViewController.m */; }; - 6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AA19EE274300767484 /* Default-667h@2x.png */; }; - 6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AB19EE274300767484 /* Default-736h@3x.png */; }; - 92F1263CECFE3FFCC7A5F936 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E8EC8300ABAAEA079224272A /* libPods-Sample.a */; }; - C081EE8D1F85AEEC00F0B5F1 /* TabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = C081EE8C1F85AEEC00F0B5F1 /* TabBarController.m */; }; - C081EE921F85AFB800F0B5F1 /* CollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C081EE8F1F85AFB800F0B5F1 /* CollectionViewController.m */; }; - C081EE931F85AFB800F0B5F1 /* TextCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = C081EE911F85AFB800F0B5F1 /* TextCellNode.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; - 05E2128119D4DB510098F589 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 05E2128519D4DB510098F589 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 05E2128619D4DB510098F589 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 05E2128819D4DB510098F589 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 05E2128919D4DB510098F589 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 05E2128B19D4DB510098F589 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; - 05E2128C19D4DB510098F589 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; - 0CDEE995962D3E4584D302EE /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = ""; }; - 6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; }; - 6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; }; - A950870A2154F92D5DC91F1A /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = ""; }; - C081EE8B1F85AEEC00F0B5F1 /* TabBarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TabBarController.h; sourceTree = ""; }; - C081EE8C1F85AEEC00F0B5F1 /* TabBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TabBarController.m; sourceTree = ""; }; - C081EE8E1F85AFB800F0B5F1 /* CollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionViewController.h; sourceTree = ""; }; - C081EE8F1F85AFB800F0B5F1 /* CollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionViewController.m; sourceTree = ""; }; - C081EE901F85AFB800F0B5F1 /* TextCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCellNode.h; sourceTree = ""; }; - C081EE911F85AFB800F0B5F1 /* TextCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextCellNode.m; sourceTree = ""; }; - E8EC8300ABAAEA079224272A /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 05E2127E19D4DB510098F589 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 92F1263CECFE3FFCC7A5F936 /* libPods-Sample.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 05E2127819D4DB510098F589 = { - isa = PBXGroup; - children = ( - 05E2128319D4DB510098F589 /* Sample */, - 05E2128219D4DB510098F589 /* Products */, - 6DE0E5D094594AB09140EF84 /* Pods */, - F5DF5EAD6C1B97F91D1C830F /* Frameworks */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - usesTabs = 0; - }; - 05E2128219D4DB510098F589 /* Products */ = { - isa = PBXGroup; - children = ( - 05E2128119D4DB510098F589 /* Sample.app */, - ); - name = Products; - sourceTree = ""; - }; - 05E2128319D4DB510098F589 /* Sample */ = { - isa = PBXGroup; - children = ( - C081EE8E1F85AFB800F0B5F1 /* CollectionViewController.h */, - C081EE8F1F85AFB800F0B5F1 /* CollectionViewController.m */, - C081EE901F85AFB800F0B5F1 /* TextCellNode.h */, - C081EE911F85AFB800F0B5F1 /* TextCellNode.m */, - 05E2128819D4DB510098F589 /* AppDelegate.h */, - 05E2128919D4DB510098F589 /* AppDelegate.m */, - 05E2128B19D4DB510098F589 /* ViewController.h */, - 05E2128C19D4DB510098F589 /* ViewController.m */, - 05E2128419D4DB510098F589 /* Supporting Files */, - C081EE8B1F85AEEC00F0B5F1 /* TabBarController.h */, - C081EE8C1F85AEEC00F0B5F1 /* TabBarController.m */, - ); - path = Sample; - sourceTree = ""; - }; - 05E2128419D4DB510098F589 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */, - 6C2C82AA19EE274300767484 /* Default-667h@2x.png */, - 6C2C82AB19EE274300767484 /* Default-736h@3x.png */, - 05E2128519D4DB510098F589 /* Info.plist */, - 05E2128619D4DB510098F589 /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 6DE0E5D094594AB09140EF84 /* Pods */ = { - isa = PBXGroup; - children = ( - 0CDEE995962D3E4584D302EE /* Pods-Sample.debug.xcconfig */, - A950870A2154F92D5DC91F1A /* Pods-Sample.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - F5DF5EAD6C1B97F91D1C830F /* Frameworks */ = { - isa = PBXGroup; - children = ( - E8EC8300ABAAEA079224272A /* libPods-Sample.a */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 05E2128019D4DB510098F589 /* Sample */ = { - isa = PBXNativeTarget; - buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */; - buildPhases = ( - 77F6A2B5E8DA12933E6365CE /* [CP] Check Pods Manifest.lock */, - 05E2127D19D4DB510098F589 /* Sources */, - 05E2127E19D4DB510098F589 /* Frameworks */, - 05E2127F19D4DB510098F589 /* Resources */, - 96436DA0C1AFF84D8041B522 /* [CP] Embed Pods Frameworks */, - D17B5BD4AA634EFE93D71E9F /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Sample; - productName = Sample; - productReference = 05E2128119D4DB510098F589 /* Sample.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 05E2127919D4DB510098F589 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0730; - ORGANIZATIONNAME = Facebook; - TargetAttributes = { - 05E2128019D4DB510098F589 = { - CreatedOnToolsVersion = 6.0.1; - }; - }; - }; - buildConfigurationList = 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 05E2127819D4DB510098F589; - productRefGroup = 05E2128219D4DB510098F589 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 05E2128019D4DB510098F589 /* Sample */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 05E2127F19D4DB510098F589 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */, - 6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */, - 6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 77F6A2B5E8DA12933E6365CE /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 96436DA0C1AFF84D8041B522 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - D17B5BD4AA634EFE93D71E9F /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 05E2127D19D4DB510098F589 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C081EE931F85AFB800F0B5F1 /* TextCellNode.m in Sources */, - 05E2128D19D4DB510098F589 /* ViewController.m in Sources */, - C081EE8D1F85AEEC00F0B5F1 /* TabBarController.m in Sources */, - 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */, - C081EE921F85AFB800F0B5F1 /* CollectionViewController.m in Sources */, - 05E2128719D4DB510098F589 /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 05E212A219D4DB510098F589 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 05E212A319D4DB510098F589 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 05E212A519D4DB510098F589 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 0CDEE995962D3E4584D302EE /* Pods-Sample.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - INFOPLIST_FILE = Sample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 05E212A619D4DB510098F589 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A950870A2154F92D5DC91F1A /* Pods-Sample.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - INFOPLIST_FILE = Sample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 05E212A219D4DB510098F589 /* Debug */, - 05E212A319D4DB510098F589 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 05E212A519D4DB510098F589 /* Debug */, - 05E212A619D4DB510098F589 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 05E2127919D4DB510098F589 /* Project object */; -} diff --git a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a62..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme deleted file mode 100644 index d41d58c5d..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples_extra/InterfaceCoalesceTest/Sample.xcworkspace/contents.xcworkspacedata b/examples_extra/InterfaceCoalesceTest/Sample.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7b5a2f305..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.h b/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.h deleted file mode 100644 index 2aa29369b..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.h +++ /dev/null @@ -1,18 +0,0 @@ -/* This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -@interface AppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow *window; - -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.m b/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.m deleted file mode 100644 index 7b8d54bd3..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/AppDelegate.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// AppDelegate.m -// Texture -// -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// - -#import "AppDelegate.h" - -#import "CollectionViewController.h" -#import "ViewController.h" - - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - self.window.backgroundColor = [UIColor whiteColor]; - - ViewController *viewController = [[ViewController alloc] init]; -// viewController.tabBarItem.title = @"TextStress"; - - - -// TabBarController *tabBarController = [[TabBarController alloc] init]; -// tabBarController.viewControllers = @[cvc, viewController]; - UINavigationController *nvc = [[UINavigationController alloc] initWithRootViewController:viewController]; - self.window.rootViewController = nvc; - [self.window makeKeyAndVisible]; - return YES; -} - -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.h b/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.h deleted file mode 100644 index 159b2fa1c..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// CollectionViewController.h -// Texture -// -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// - -#import -#import - -@interface CollectionViewController : ASViewController -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.m b/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.m deleted file mode 100644 index d93005236..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/CollectionViewController.m +++ /dev/null @@ -1,67 +0,0 @@ -// -// CollectionViewController.m -// Texture -// -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// - -#import "CollectionViewController.h" -#import "TextCellNode.h" - -@interface CollectionViewController() -{ - ASCollectionNode *_collectionNode; - NSArray *_labels; - TextCellNode *_cellNode; -} - -@end - -@implementation CollectionViewController - -- (instancetype)init -{ - UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; - _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:flowLayout]; - CGRect rect = [[UIApplication sharedApplication] statusBarFrame]; - _collectionNode.contentInset = UIEdgeInsetsMake(rect.size.height, 0, 0, 0); - self = [super initWithNode:_collectionNode]; - if (self) { - _collectionNode.delegate = self; - _collectionNode.dataSource = self; - } - return self; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - _collectionNode.backgroundColor = [UIColor whiteColor]; - _labels = @[@"Fight of the Living Dead: Experiment Fight of the Living Dead: Experiment", @"S1 • E1"]; -} - -- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section -{ - return 1; -} - -- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath -{ - return ^{ - _cellNode = [[TextCellNode alloc] initWithText1:_labels[0] text2:_labels[1]]; - return _cellNode; - }; -} - -- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath -{ - CGFloat width = collectionNode.view.bounds.size.width; - return ASSizeRangeMake(CGSizeMake(width, 0.0f), CGSizeMake(width, CGFLOAT_MAX)); -} - -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/Info.plist b/examples_extra/InterfaceCoalesceTest/Sample/Info.plist deleted file mode 100644 index fb4115c84..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.h b/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.h deleted file mode 100644 index 5a25bb04a..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// TabBarController.h -// Texture -// -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// - -#import - -@interface TabBarController : ASTabBarController -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.m b/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.m deleted file mode 100644 index a7905cb23..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/TabBarController.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// TabBarController.m -// Texture -// -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// - -#import "TabBarController.h" - -@interface TabBarController () -@end - -@implementation TabBarController -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.h b/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.h deleted file mode 100644 index c4b35cfaf..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// TextCellNode.h -// Texture -// -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// - -#import - -@interface TextCellNode : ASCellNode -- (instancetype)initWithText1:(NSString *)text1 text2:(NSString *)text2; -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.m b/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.m deleted file mode 100644 index 13bb7694c..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/TextCellNode.m +++ /dev/null @@ -1,100 +0,0 @@ -// -// TextCellNode.m -// Texture -// -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// - -#import "TextCellNode.h" -#import -#import - -#ifndef USE_ASTEXTNODE_2 -#define USE_ASTEXTNODE_2 1 -#endif - -@interface TextCellNode() -{ -#if USE_ASTEXTNODE_2 - ASTextNode2 *_label1; - ASTextNode2 *_label2; -#else - ASTextNode *_label1; - ASTextNode *_label2; -#endif -} -@end - -@implementation TextCellNode - -- (instancetype)initWithText1:(NSString *)text1 text2:(NSString *)text2 -{ - self = [super init]; - if (self) { - self.automaticallyManagesSubnodes = YES; - self.clipsToBounds = YES; -#if USE_ASTEXTNODE_2 - _label1 = [[ASTextNode2 alloc] init]; - _label2 = [[ASTextNode2 alloc] init]; -#else - _label1 = [[ASTextNode alloc] init]; - _label2 = [[ASTextNode alloc] init]; -#endif - - _label1.attributedText = [[NSAttributedString alloc] initWithString:text1]; - _label2.attributedText = [[NSAttributedString alloc] initWithString:text2]; - - _label1.maximumNumberOfLines = 1; - _label1.truncationMode = NSLineBreakByTruncatingTail; - _label2.maximumNumberOfLines = 1; - _label2.truncationMode = NSLineBreakByTruncatingTail; - - [self simpleSetupYogaLayout]; - } - return self; -} - -/** - This is to text a row with two labels, the first should be truncated with "...". - Layout is like: [l1Container[_label1], label2]. - This shows a bug of ASTextNode2. - */ -- (void)simpleSetupYogaLayout -{ - [self.style yogaNodeCreateIfNeeded]; - [_label1.style yogaNodeCreateIfNeeded]; - [_label2.style yogaNodeCreateIfNeeded]; - - _label1.style.flexGrow = 0; - _label1.style.flexShrink = 1; - _label1.backgroundColor = [UIColor lightGrayColor]; - - _label2.style.flexGrow = 0; - _label2.style.flexShrink = 0; - _label2.backgroundColor = [UIColor greenColor]; - - ASDisplayNode *l1Container = [ASDisplayNode yogaVerticalStack]; - - // TODO(fix ASTextNode2): next two line will show the bug of TextNode2 - // which works for ASTextNode though - // see discussion here: https://github.com/TextureGroup/Texture/pull/553 - l1Container.style.alignItems = ASStackLayoutAlignItemsCenter; - _label1.style.alignSelf = ASStackLayoutAlignSelfStart; - - l1Container.style.flexGrow = 0; - l1Container.style.flexShrink = 1; - - l1Container.yogaChildren = @[_label1]; - - self.style.justifyContent = ASStackLayoutJustifyContentSpaceBetween; - self.style.alignItems = ASStackLayoutAlignItemsStart; - self.style.flexDirection = ASStackLayoutDirectionHorizontal; - self.yogaChildren = @[l1Container, _label2]; -} - -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/ViewController.h b/examples_extra/InterfaceCoalesceTest/Sample/ViewController.h deleted file mode 100644 index d0e9200d8..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/ViewController.h +++ /dev/null @@ -1,16 +0,0 @@ -/* This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -@interface ViewController : UIViewController - -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/ViewController.m b/examples_extra/InterfaceCoalesceTest/Sample/ViewController.m deleted file mode 100644 index 8968fadd1..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/ViewController.m +++ /dev/null @@ -1,47 +0,0 @@ -/* This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import "ViewController.h" -#import - -#define NUMBER_ELEMENTS 2 - -@interface ViewController () -{ - ASButtonNode *_node; -} - -@end - - -@implementation ViewController - -+(void)initialize { - [[ASCATransactionQueue sharedQueue] disableInterfaceStateCoalesce]; -} -- (void)viewDidLoad -{ - [super viewDidLoad]; - - _node = [[ASButtonNode alloc] init]; - _node.frame = CGRectMake(0, 100, 100, 100); - _node.backgroundColor = [UIColor greenColor]; - - [self.view addSubnode:_node]; - [_node addTarget:self action:@selector(clicked:) forControlEvents:ASControlNodeEventTouchUpInside]; -} - -- (void)clicked:(id)sender { - ViewController *vc = [[ViewController alloc] init]; - [self.navigationController pushViewController:vc animated:YES]; -} - -@end diff --git a/examples_extra/InterfaceCoalesceTest/Sample/main.m b/examples_extra/InterfaceCoalesceTest/Sample/main.m deleted file mode 100644 index ae9488711..000000000 --- a/examples_extra/InterfaceCoalesceTest/Sample/main.m +++ /dev/null @@ -1,20 +0,0 @@ -/* This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} From 7306dc58b38a26080010427f2a5d711f385212ea Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 4 Feb 2018 16:32:32 -0800 Subject: [PATCH 08/22] merge range managed and none range managed for didExitHierarchy --- Source/ASDisplayNode.mm | 57 +++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 76ad09182..9481c5ef3 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2786,39 +2786,34 @@ - (void)didExitHierarchy ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode"); ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - - if (![self supportsRangeManagedInterfaceState]) { - // TODO: Consider merging this codepath with the below, so both range-managed and standalone - // nodes wait before firing their exit-visibility handlers. This is more important than it used - // to be, as UIViewController transitions now do rehosting at both start & end of animation. - self.interfaceState = ASInterfaceStateNone; - } else { - // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for - // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. - // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the - // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). - // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer - // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. - - if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { - void(^exitVisibleInterfaceState)(void) = ^{ - // This block intentionally retains self. - __instanceLock__.lock(); - unsigned isStillInHierarchy = _flags.isInHierarchy; - BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); - ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); - __instanceLock__.unlock(); - - if (!isStillInHierarchy && isVisible) { - self.interfaceState = newState; - }; - }; - if ([[ASCATransactionQueue sharedQueue] disabled]) { - dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); - } else { - exitVisibleInterfaceState(); + // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for + // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. + // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the + // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). + // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer + // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. + if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { + void(^exitVisibleInterfaceState)(void) = ^{ + // This block intentionally retains self. + __instanceLock__.lock(); + unsigned isStillInHierarchy = _flags.isInHierarchy; + BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); + ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); + __instanceLock__.unlock(); + + if (!isStillInHierarchy && isVisible) { + if (![self supportsRangeManagedInterfaceState]) { + newState = ASInterfaceStateNone; + } + self.interfaceState = newState; } + }; + + if ([[ASCATransactionQueue sharedQueue] disabled]) { + dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); + } else { + exitVisibleInterfaceState(); } } } From cfd083af7dfe71763dca33c8544b92fcae50467f Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 4 Feb 2018 16:39:52 -0800 Subject: [PATCH 09/22] remove metadata --- .../Sample.xcworkspace/contents.xcworkspacedata | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 examples/CatDealsCollectionView/Sample.xcworkspace/contents.xcworkspacedata diff --git a/examples/CatDealsCollectionView/Sample.xcworkspace/contents.xcworkspacedata b/examples/CatDealsCollectionView/Sample.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7b5a2f305..000000000 --- a/examples/CatDealsCollectionView/Sample.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - From 1ec63b60c53fff00776d4cf43106931a2c486dd8 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 4 Feb 2018 16:50:47 -0800 Subject: [PATCH 10/22] abstract queue to impl class methods --- Source/ASRunLoopQueue.h | 7 ++- Source/ASRunLoopQueue.mm | 109 +++++++++++++++------------------------ 2 files changed, 47 insertions(+), 69 deletions(-) diff --git a/Source/ASRunLoopQueue.h b/Source/ASRunLoopQueue.h index 87c614db5..f337c95af 100644 --- a/Source/ASRunLoopQueue.h +++ b/Source/ASRunLoopQueue.h @@ -24,8 +24,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)prepareForCATransactionCommit; @end +@interface ASAbstractRunLoopQueue : NSObject +@end + AS_SUBCLASSING_RESTRICTED -@interface ASRunLoopQueue : NSObject +@interface ASRunLoopQueue : ASAbstractRunLoopQueue /** * Create a new queue with the given run loop and handler. @@ -53,7 +56,7 @@ AS_SUBCLASSING_RESTRICTED @end AS_SUBCLASSING_RESTRICTED -@interface ASCATransactionQueue : NSObject +@interface ASCATransactionQueue : ASAbstractRunLoopQueue @property (nonatomic, readonly) BOOL isEmpty; @property (nonatomic, readonly) BOOL disabled; diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 389eb7215..41f3d3199 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -186,27 +186,6 @@ - (void)dealloc @end -#pragma mark - ASRunLoopQueue - -@interface ASRunLoopQueue () { - CFRunLoopRef _runLoop; - CFRunLoopSourceRef _runLoopSource; - CFRunLoopObserverRef _runLoopObserver; - NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance. - ASDN::RecursiveMutex _internalQueueLock; - - // In order to not pollute the top-level activities, each queue has 1 root activity. - os_activity_t _rootActivity; - -#if ASRunLoopQueueLoggingEnabled - NSTimer *_runloopQueueLoggingTimer; -#endif -} - -@property (nonatomic, copy) void (^queueConsumer)(id dequeuedItem, BOOL isQueueDrained); - -@end - #if AS_KDEBUG_ENABLE /** * This is real, private CA API. Valid as of iOS 10. @@ -223,7 +202,23 @@ + (int)currentState; @end #endif -@implementation ASRunLoopQueue +#pragma mark - ASAbstractRunLoopQueue + +@interface ASAbstractRunLoopQueue (Private) ++ (void)load; ++ (void)registerCATransactionObservers; +@end + +@implementation ASAbstractRunLoopQueue + +- (instancetype)init +{ + if (self != [super init]) { + return nil; + } + ASDisplayNodeAssert(self.class != [ASAbstractRunLoopQueue class], @"Should never create instances of abstract class ASAbstractRunLoopQueue."); + return self; +} #if AS_KDEBUG_ENABLE + (void)load @@ -270,6 +265,31 @@ + (void)registerCATransactionObservers #endif // AS_KDEBUG_ENABLE +@end + +#pragma mark - ASRunLoopQueue + +@interface ASRunLoopQueue () { + CFRunLoopRef _runLoop; + CFRunLoopSourceRef _runLoopSource; + CFRunLoopObserverRef _runLoopObserver; + NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance. + ASDN::RecursiveMutex _internalQueueLock; + + // In order to not pollute the top-level activities, each queue has 1 root activity. + os_activity_t _rootActivity; + +#if ASRunLoopQueueLoggingEnabled + NSTimer *_runloopQueueLoggingTimer; +#endif +} + +@property (nonatomic, copy) void (^queueConsumer)(id dequeuedItem, BOOL isQueueDrained); + +@end + +@implementation ASRunLoopQueue + - (instancetype)initWithRunLoop:(CFRunLoopRef)runloop retainObjects:(BOOL)retainsObjects handler:(void (^)(id _Nullable, BOOL))handlerBlock { if (self = [super init]) { @@ -496,51 +516,6 @@ @implementation ASCATransactionQueue // but after most other scheduled work on the runloop has processed. static int const kASASCATransactionQueueOrder = 1000000; -#if AS_KDEBUG_ENABLE -+ (void)load -{ - [self registerCATransactionObservers]; -} - -+ (void)registerCATransactionObservers -{ - static BOOL privateCAMethodsExist; - static dispatch_block_t preLayoutHandler; - static dispatch_block_t preCommitHandler; - static dispatch_block_t postCommitHandler; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - privateCAMethodsExist = [CATransaction respondsToSelector:@selector(addCommitHandler:forPhase:)]; - privateCAMethodsExist &= [CATransaction respondsToSelector:@selector(currentState)]; - if (!privateCAMethodsExist) { - NSLog(@"Private CA methods are gone."); - } - preLayoutHandler = ^{ - ASSignpostStartCustom(ASSignpostCATransactionLayout, 0, [CATransaction currentState]); - }; - preCommitHandler = ^{ - int state = [CATransaction currentState]; - ASSignpostEndCustom(ASSignpostCATransactionLayout, 0, state, ASSignpostColorDefault); - ASSignpostStartCustom(ASSignpostCATransactionCommit, 0, state); - }; - postCommitHandler = ^{ - ASSignpostEndCustom(ASSignpostCATransactionCommit, 0, [CATransaction currentState], ASSignpostColorDefault); - // Can't add new observers inside an observer. rdar://problem/31253952 - dispatch_async(dispatch_get_main_queue(), ^{ - [self registerCATransactionObservers]; - }); - }; - }); - - if (privateCAMethodsExist) { - [CATransaction addCommitHandler:preLayoutHandler forPhase:kCATransactionPhasePreLayout]; - [CATransaction addCommitHandler:preCommitHandler forPhase:kCATransactionPhasePreCommit]; - [CATransaction addCommitHandler:postCommitHandler forPhase:kCATransactionPhasePostCommit]; - } -} - -#endif // AS_KDEBUG_ENABLE - + (ASCATransactionQueue *)sharedQueue { static dispatch_once_t onceToken; From 58effec5b61e5d3380e33c87958db3492e6aa8c7 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 4 Feb 2018 17:11:20 -0800 Subject: [PATCH 11/22] Add tests --- Tests/ASRunLoopQueueTests.m | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Tests/ASRunLoopQueueTests.m b/Tests/ASRunLoopQueueTests.m index ccf15ae7b..3cb4dd2e4 100644 --- a/Tests/ASRunLoopQueueTests.m +++ b/Tests/ASRunLoopQueueTests.m @@ -12,9 +12,21 @@ #import #import +#import "ASDisplayNodeTestsHelper.h" static NSTimeInterval const kRunLoopRunTime = 0.001; // Allow the RunLoop to run for one millisecond each time. +@interface QueueObject : NSObject +@property (nonatomic, assign) BOOL queueObjectProcessed; +@end + +@implementation QueueObject +- (void)prepareForCATransactionCommit +{ + self.queueObjectProcessed = YES; +} +@end + @interface ASRunLoopQueueTests : XCTestCase @end @@ -157,4 +169,20 @@ - (void)testWeakQueueWithAllDeallocatedObjectsIsDrained XCTAssertTrue(queue.isEmpty); } +- (void)testASCATransactionQueueDisable { + [[ASCATransactionQueue sharedQueue] disableInterfaceStateCoalesce]; + QueueObject *object = [[QueueObject alloc] init]; + [[ASCATransactionQueue sharedQueue] enqueue:object]; + XCTAssertTrue([[ASCATransactionQueue sharedQueue] isEmpty]); + XCTAssertTrue([[ASCATransactionQueue sharedQueue] disabled]); +} + +- (void)testASCATransactionQueueProcess { + QueueObject *object = [[QueueObject alloc] init]; + [[ASCATransactionQueue sharedQueue] enqueue:object]; + XCTAssertFalse(object.queueObjectProcessed); + ASCATransactionQueueWait(); + XCTAssertTrue(object.queueObjectProcessed); +} + @end From cace5357b884da1fb57a726ede3542a59df70642 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 4 Feb 2018 23:09:34 -0800 Subject: [PATCH 12/22] Fix test fail because of shared object. --- Tests/ASRunLoopQueueTests.m | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Tests/ASRunLoopQueueTests.m b/Tests/ASRunLoopQueueTests.m index 3cb4dd2e4..fdbdd3cab 100644 --- a/Tests/ASRunLoopQueueTests.m +++ b/Tests/ASRunLoopQueueTests.m @@ -169,17 +169,21 @@ - (void)testWeakQueueWithAllDeallocatedObjectsIsDrained XCTAssertTrue(queue.isEmpty); } -- (void)testASCATransactionQueueDisable { - [[ASCATransactionQueue sharedQueue] disableInterfaceStateCoalesce]; +- (void)testASCATransactionQueueDisable +{ + ASCATransactionQueue *queue = [[ASCATransactionQueue alloc] init]; + [queue disableInterfaceStateCoalesce]; QueueObject *object = [[QueueObject alloc] init]; [[ASCATransactionQueue sharedQueue] enqueue:object]; - XCTAssertTrue([[ASCATransactionQueue sharedQueue] isEmpty]); - XCTAssertTrue([[ASCATransactionQueue sharedQueue] disabled]); + XCTAssertTrue([queue isEmpty]); + XCTAssertTrue([queue disabled]); } -- (void)testASCATransactionQueueProcess { +- (void)testASCATransactionQueueProcess +{ + ASCATransactionQueue *queue = [[ASCATransactionQueue alloc] init]; QueueObject *object = [[QueueObject alloc] init]; - [[ASCATransactionQueue sharedQueue] enqueue:object]; + [queue enqueue:object]; XCTAssertFalse(object.queueObjectProcessed); ASCATransactionQueueWait(); XCTAssertTrue(object.queueObjectProcessed); From 06313724a12cc6f7252c5146731cd5710cbe65a7 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Mon, 5 Feb 2018 10:19:15 -0800 Subject: [PATCH 13/22] guard _pendingInterfaceState access with lock --- Source/ASDisplayNode.mm | 10 +++++++--- Source/Private/ASDisplayNode+FrameworkPrivate.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 9481c5ef3..b828f446d 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2741,9 +2741,7 @@ - (void)setHierarchyState:(ASHierarchyState)newState // Entered or exited range managed state. if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) { if (newState & ASHierarchyStateRangeManaged) { - if (self.supernode) { - [self enterInterfaceState:self.supernode->_pendingInterfaceState]; - } + [self enterInterfaceState:[self.supernode _locked_pendingInterfaceState]]; } else { // The case of exiting a range-managed state should be fairly rare. Adding or removing the node // to a view hierarchy will cause its interfaceState to be either fully set or unset (all fields), @@ -2887,6 +2885,12 @@ - (void)setInterfaceState:(ASInterfaceState)newState } } +- (ASInterfaceState)_locked_pendingInterfaceState +{ + ASDN::MutexLocker l(__instanceLock__); + return _pendingInterfaceState; +} + - (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState { //This method is currently called on the main thread. The assert has been added here because all of the diff --git a/Source/Private/ASDisplayNode+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h index dbb520787..c1e5d26be 100644 --- a/Source/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -150,6 +150,8 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarc - (void)enterHierarchyState:(ASHierarchyState)hierarchyState; - (void)exitHierarchyState:(ASHierarchyState)hierarchyState; +- (ASInterfaceState)_locked_pendingInterfaceState; + // Changed before calling willEnterHierarchy / didExitHierarchy. @property (readonly, assign, getter = isInHierarchy) BOOL inHierarchy; // Call willEnterHierarchy if necessary and set inHierarchy = YES if visibility notifications are enabled on all of its parents From f3c8507c88913d468e82e8b3113b463d38cb4743 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Mon, 5 Feb 2018 17:13:56 -0800 Subject: [PATCH 14/22] name refactor --- Source/ASDisplayNode.mm | 4 ++-- Source/Private/ASDisplayNode+FrameworkPrivate.h | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index b828f446d..4a888a59f 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2741,7 +2741,7 @@ - (void)setHierarchyState:(ASHierarchyState)newState // Entered or exited range managed state. if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) { if (newState & ASHierarchyStateRangeManaged) { - [self enterInterfaceState:[self.supernode _locked_pendingInterfaceState]]; + [self enterInterfaceState:self.supernode.pendingInterfaceState]; } else { // The case of exiting a range-managed state should be fairly rare. Adding or removing the node // to a view hierarchy will cause its interfaceState to be either fully set or unset (all fields), @@ -2885,7 +2885,7 @@ - (void)setInterfaceState:(ASInterfaceState)newState } } -- (ASInterfaceState)_locked_pendingInterfaceState +- (ASInterfaceState)pendingInterfaceState { ASDN::MutexLocker l(__instanceLock__); return _pendingInterfaceState; diff --git a/Source/Private/ASDisplayNode+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h index c1e5d26be..b309bdcc2 100644 --- a/Source/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -141,6 +141,10 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarc // delegate to inform of ASInterfaceState changes (used by ASNodeController) @property (nonatomic, weak) id interfaceStateDelegate; +// The -pendingInterfaceState holds the value that will be applied to -interfaceState by the +// ASCATransactionQueue. If already applied, it matches -interfaceState. Thread-safe access. +@property (nonatomic, readonly) ASInterfaceState pendingInterfaceState; + // These methods are recursive, and either union or remove the provided interfaceState to all sub-elements. - (void)enterInterfaceState:(ASInterfaceState)interfaceState; - (void)exitInterfaceState:(ASInterfaceState)interfaceState; @@ -150,8 +154,6 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarc - (void)enterHierarchyState:(ASHierarchyState)hierarchyState; - (void)exitHierarchyState:(ASHierarchyState)hierarchyState; -- (ASInterfaceState)_locked_pendingInterfaceState; - // Changed before calling willEnterHierarchy / didExitHierarchy. @property (readonly, assign, getter = isInHierarchy) BOOL inHierarchy; // Call willEnterHierarchy if necessary and set inHierarchy = YES if visibility notifications are enabled on all of its parents From e2c7d06253d03df378adb94753a23fcdeb0bc063 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Wed, 7 Feb 2018 23:32:46 -0800 Subject: [PATCH 15/22] Refactor from comments https://github.com/TextureGroup/Texture/pull/788/\#pullrequestreview-94849919 --- Source/ASDisplayNode.mm | 1 - Source/ASRunLoopQueue.h | 10 +++++----- Source/ASRunLoopQueue.mm | 10 ++++------ Tests/ASRunLoopQueueTests.m | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 4a888a59f..6077bbf05 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2873,7 +2873,6 @@ - (ASInterfaceState)interfaceState - (void)setInterfaceState:(ASInterfaceState)newState { - ASDN::MutexLocker l(__instanceLock__); if ([[ASCATransactionQueue sharedQueue] disabled]) { [self applyPendingInterfaceState:newState]; } else { diff --git a/Source/ASRunLoopQueue.h b/Source/ASRunLoopQueue.h index f337c95af..02711910b 100644 --- a/Source/ASRunLoopQueue.h +++ b/Source/ASRunLoopQueue.h @@ -48,7 +48,7 @@ AS_SUBCLASSING_RESTRICTED - (void)enqueue:(ObjectType)object; -@property (nonatomic, readonly) BOOL isEmpty; +@property (atomic, readonly) BOOL isEmpty; @property (nonatomic, assign) NSUInteger batchSize; // Default == 1. @property (nonatomic, assign) BOOL ensureExclusiveMembership; // Default == YES. Set-like behavior. @@ -58,8 +58,8 @@ AS_SUBCLASSING_RESTRICTED AS_SUBCLASSING_RESTRICTED @interface ASCATransactionQueue : ASAbstractRunLoopQueue -@property (nonatomic, readonly) BOOL isEmpty; -@property (nonatomic, readonly) BOOL disabled; +@property (atomic, readonly) BOOL isEmpty; +@property (atomic, readonly) BOOL disabled; /** * The queue to run on main run loop before CATransaction commit. * @@ -67,14 +67,14 @@ AS_SUBCLASSING_RESTRICTED * to get last chance of updating/coalesce info like interface state. * Each node will only be called once per transaction commit to reflect interface change. */ -+ (ASCATransactionQueue *)sharedQueue; +@property (class, atomic, readonly) ASCATransactionQueue *sharedQueue; - (void)enqueue:(id)object; /** * @abstract Apply a node's interfaceState immediately rather than adding to the queue. */ -- (void)disableInterfaceStateCoalesce; +- (void)disable; @end diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 41f3d3199..7131bc0ae 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -496,7 +496,7 @@ @interface ASCATransactionQueue () { CFRunLoopRef _runLoop; CFRunLoopSourceRef _runLoopSource; CFRunLoopObserverRef _runLoopObserver; - NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance. + NSPointerArray *_internalQueue; ASDN::RecursiveMutex _internalQueueLock; BOOL _disableInterfaceStateCoalesce; @@ -571,9 +571,7 @@ - (instancetype)init - (void)dealloc { - if (CFRunLoopContainsSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes)) { - CFRunLoopRemoveSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes); - } + CFRunLoopRemoveSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes); CFRelease(_runLoopSource); _runLoopSource = nil; @@ -674,7 +672,7 @@ - (void)enqueue:(id)object return; } - if (_disableInterfaceStateCoalesce == YES) { + if (_disableInterfaceStateCoalesce) { [object prepareForCATransactionCommit]; return; } @@ -705,7 +703,7 @@ - (BOOL)isEmpty return _internalQueue.count == 0; } -- (void)disableInterfaceStateCoalesce +- (void)disable { _disableInterfaceStateCoalesce = YES; } diff --git a/Tests/ASRunLoopQueueTests.m b/Tests/ASRunLoopQueueTests.m index fdbdd3cab..8f7553b74 100644 --- a/Tests/ASRunLoopQueueTests.m +++ b/Tests/ASRunLoopQueueTests.m @@ -172,7 +172,7 @@ - (void)testWeakQueueWithAllDeallocatedObjectsIsDrained - (void)testASCATransactionQueueDisable { ASCATransactionQueue *queue = [[ASCATransactionQueue alloc] init]; - [queue disableInterfaceStateCoalesce]; + [queue disable]; QueueObject *object = [[QueueObject alloc] init]; [[ASCATransactionQueue sharedQueue] enqueue:object]; XCTAssertTrue([queue isEmpty]); From 7556ed79e358dcbeb6c8962871c194aed2f3ddef Mon Sep 17 00:00:00 2001 From: Max Wang Date: Thu, 8 Feb 2018 23:34:37 -0800 Subject: [PATCH 16/22] Apply InterfaceState immediately after ASCATranactionQueue is processed and before next runloop started. --- Source/ASDisplayNode.mm | 1 + Source/ASRunLoopQueue.mm | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 6077bbf05..3f8fa081a 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -3017,6 +3017,7 @@ - (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState - (void)prepareForCATransactionCommit { + // Apply _pendingInterfaceState actual _interfaceState, note that ASInterfaceStateNone is not used. [self applyPendingInterfaceState:ASInterfaceStateNone]; } diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 7131bc0ae..de7cbd201 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -496,9 +496,11 @@ @interface ASCATransactionQueue () { CFRunLoopRef _runLoop; CFRunLoopSourceRef _runLoopSource; CFRunLoopObserverRef _runLoopObserver; + CFRunLoopObserverRef _runLoopPostObserver; NSPointerArray *_internalQueue; ASDN::RecursiveMutex _internalQueueLock; BOOL _disableInterfaceStateCoalesce; + BOOL _postCATransactionCommit; // In order to not pollute the top-level activities, each queue has 1 root activity. os_activity_t _rootActivity; @@ -515,6 +517,9 @@ @implementation ASCATransactionQueue // CoreAnimation commit order is 2000000, the goal of this is to process shortly beforehand // but after most other scheduled work on the runloop has processed. static int const kASASCATransactionQueueOrder = 1000000; +// This will mark the end of current loop and any node enqueued between kASASCATransactionQueueOrder +// and kASASCATransactionQueuePostOrder will apply interface change immediately. +static int const kASASCATransactionQueuePostOrder = 3000000; + (ASCATransactionQueue *)sharedQueue { @@ -548,8 +553,14 @@ - (instancetype)init void (^handlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { [weakSelf processQueue]; }; + void (^postHandlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + _postCATransactionCommit = NO; + }; _runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueueOrder, handlerBlock); + _runLoopPostObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueuePostOrder, postHandlerBlock); + CFRunLoopAddObserver(_runLoop, _runLoopObserver, kCFRunLoopCommonModes); + CFRunLoopAddObserver(_runLoop, _runLoopPostObserver, kCFRunLoopCommonModes); // It is not guaranteed that the runloop will turn if it has no scheduled work, and this causes processing of // the queue to stop. Attaching a custom loop source to the run loop and signal it if new work needs to be done @@ -663,6 +674,15 @@ - (void)processQueue CFRunLoopWakeUp(_runLoop); } + ASDN::MutexLocker l(_internalQueueLock); + if (_internalQueue.count != 0) { + [self processQueue]; + } + // Mark the queue will end coalescing shortly until after CATransactionCommit. + // This will give the queue a chance to apply any further interfaceState changes/enqueue + // immediately within current runloop instead of pushing the work to next runloop cycle. + _postCATransactionCommit = YES; + ASSignpostEnd(ASSignpostRunLoopQueueBatch); } @@ -672,7 +692,7 @@ - (void)enqueue:(id)object return; } - if (_disableInterfaceStateCoalesce) { + if (_disableInterfaceStateCoalesce || _postCATransactionCommit) { [object prepareForCATransactionCommit]; return; } From 09044223f80a70a398a9d770d0787e2efa6b973b Mon Sep 17 00:00:00 2001 From: Max Wang Date: Fri, 9 Feb 2018 14:10:43 -0800 Subject: [PATCH 17/22] refactor --- Source/ASRunLoopQueue.mm | 55 ++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index de7cbd201..8f6478da8 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -495,12 +495,12 @@ - (void)unlock @interface ASCATransactionQueue () { CFRunLoopRef _runLoop; CFRunLoopSourceRef _runLoopSource; - CFRunLoopObserverRef _runLoopObserver; - CFRunLoopObserverRef _runLoopPostObserver; + CFRunLoopObserverRef _preTransactionObserver; + CFRunLoopObserverRef _postTransactionObserver; NSPointerArray *_internalQueue; ASDN::RecursiveMutex _internalQueueLock; BOOL _disableInterfaceStateCoalesce; - BOOL _postCATransactionCommit; + BOOL _CATransactionCommitInProgress; // In order to not pollute the top-level activities, each queue has 1 root activity. os_activity_t _rootActivity; @@ -554,13 +554,14 @@ - (instancetype)init [weakSelf processQueue]; }; void (^postHandlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { - _postCATransactionCommit = NO; + ASDN::MutexLocker l(_internalQueueLock); + _CATransactionCommitInProgress = NO; }; - _runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueueOrder, handlerBlock); - _runLoopPostObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueuePostOrder, postHandlerBlock); + _preTransactionObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueueOrder, handlerBlock); + _postTransactionObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueuePostOrder, postHandlerBlock); - CFRunLoopAddObserver(_runLoop, _runLoopObserver, kCFRunLoopCommonModes); - CFRunLoopAddObserver(_runLoop, _runLoopPostObserver, kCFRunLoopCommonModes); + CFRunLoopAddObserver(_runLoop, _preTransactionObserver, kCFRunLoopCommonModes); + CFRunLoopAddObserver(_runLoop, _postTransactionObserver, kCFRunLoopCommonModes); // It is not guaranteed that the runloop will turn if it has no scheduled work, and this causes processing of // the queue to stop. Attaching a custom loop source to the run loop and signal it if new work needs to be done @@ -586,11 +587,16 @@ - (void)dealloc CFRelease(_runLoopSource); _runLoopSource = nil; - if (CFRunLoopObserverIsValid(_runLoopObserver)) { - CFRunLoopObserverInvalidate(_runLoopObserver); + if (CFRunLoopObserverIsValid(_preTransactionObserver)) { + CFRunLoopObserverInvalidate(_preTransactionObserver); } - CFRelease(_runLoopObserver); - _runLoopObserver = nil; + if (CFRunLoopObserverIsValid(_postTransactionObserver)) { + CFRunLoopObserverInvalidate(_postTransactionObserver); + } + CFRelease(_preTransactionObserver); + CFRelease(_postTransactionObserver); + _preTransactionObserver = nil; + _postTransactionObserver = nil; } #if ASRunLoopQueueLoggingEnabled @@ -610,6 +616,11 @@ - (void)processQueue { ASDN::MutexLocker l(_internalQueueLock); + // Mark the queue will end coalescing shortly until after CATransactionCommit. + // This will give the queue a chance to apply any further interfaceState changes/enqueue + // immediately within current runloop instead of pushing the work to next runloop cycle. + _CATransactionCommitInProgress = YES; + NSInteger internalQueueCount = _internalQueue.count; // Early-exit if the queue is empty. if (internalQueueCount == 0) { @@ -648,9 +659,6 @@ - (void)processQueue } [_internalQueue compact]; - if (_internalQueue.count == 0) { - isQueueDrained = YES; - } } // itemsToProcess will be empty if _queueConsumer == nil so no need to check again. @@ -668,21 +676,6 @@ - (void)processQueue } } - // If the queue is not fully drained yet force another run loop to process next batch of items - if (!isQueueDrained) { - CFRunLoopSourceSignal(_runLoopSource); - CFRunLoopWakeUp(_runLoop); - } - - ASDN::MutexLocker l(_internalQueueLock); - if (_internalQueue.count != 0) { - [self processQueue]; - } - // Mark the queue will end coalescing shortly until after CATransactionCommit. - // This will give the queue a chance to apply any further interfaceState changes/enqueue - // immediately within current runloop instead of pushing the work to next runloop cycle. - _postCATransactionCommit = YES; - ASSignpostEnd(ASSignpostRunLoopQueueBatch); } @@ -692,7 +685,7 @@ - (void)enqueue:(id)object return; } - if (_disableInterfaceStateCoalesce || _postCATransactionCommit) { + if (_disableInterfaceStateCoalesce || _CATransactionCommitInProgress) { [object prepareForCATransactionCommit]; return; } From 1efd5e8b46eb181fc82826efcbd0faa5734b5b9f Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sat, 10 Feb 2018 22:27:24 -0800 Subject: [PATCH 18/22] no op to start CI build --- Tests/ASCollectionViewTests.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm index eddf39266..c3dc8a2c4 100644 --- a/Tests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -1050,7 +1050,7 @@ - (void)testInitialRangeBounds window.rootViewController = testController; [window makeKeyAndVisible]; - // Trigger the initial reload to start + // Trigger the initial reload to start [window layoutIfNeeded]; // Test the APIs that monitor ASCollectionNode update handling From 632c69fc9c17771d2716dd384e4d6ab12046b2c0 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 11 Feb 2018 10:09:29 -0800 Subject: [PATCH 19/22] remove unused var and kick off tests --- Source/ASRunLoopQueue.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 4927b3dac..089e1a49d 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -607,7 +607,6 @@ - (void)processQueue // This is to avoid needlessly retaining/releasing the objects if we don't have a block. std::vector itemsToProcess; - BOOL isQueueDrained = NO; { ASDN::MutexLocker l(_internalQueueLock); From 9dad5a5a3627d3e609fecbff36611c9004ff89c4 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Sun, 11 Feb 2018 11:32:36 -0800 Subject: [PATCH 20/22] change lisence --- Tests/ASRunLoopQueueTests.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Tests/ASRunLoopQueueTests.m b/Tests/ASRunLoopQueueTests.m index 8f7553b74..485911154 100644 --- a/Tests/ASRunLoopQueueTests.m +++ b/Tests/ASRunLoopQueueTests.m @@ -2,10 +2,9 @@ // ASRunLoopQueueTests.m // Texture // -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); +// Copyright (c) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. -// You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // From a6a7e0a2382eedda315bdbf6293d95636d5b6231 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Mon, 12 Feb 2018 21:51:53 -0800 Subject: [PATCH 21/22] remove code for weak ref --- Source/ASRunLoopQueue.mm | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 089e1a49d..12265d1df 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -644,14 +644,6 @@ - (void)processQueue } } - if (foundItemCount == 0) { - // If _internalQueue holds weak references, and all of them just become NULL, then the array - // is never marked as needsCompletion, and compact will return early, not removing the NULL's. - // Inserting a NULL here ensures the compaction will take place. - // See http://www.openradar.me/15396578 and https://stackoverflow.com/a/40274426/1136669 - [_internalQueue addPointer:NULL]; - } - [_internalQueue compact]; } From 19bdef2cdd1709e10b032302de3cb4ade40560b2 Mon Sep 17 00:00:00 2001 From: Max Wang Date: Mon, 12 Feb 2018 21:58:16 -0800 Subject: [PATCH 22/22] add change log and adjust license --- CHANGELOG.md | 1 + Tests/ASRunLoopQueueTests.m | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b50cd3a5..18d54706a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## master * Add your own contributions to the next release on the line below this with your name. +- [ASRunloopQueue] Introduce new runloop queue(ASCATransactionQueue) to coalesce Interface state update calls for view controller transitions. - [ASRangeController] Fix stability of "minimum" rangeMode if the app has more than one layout before scrolling. - **Important** ASDisplayNode's cornerRadius is a new thread-safe bridged property that should be preferred over CALayer's. Use the latter at your own risk! [Huy Nguyen](https://github.com/nguyenhuy) [#749](https://github.com/TextureGroup/Texture/pull/749). - [ASCellNode] Adds mapping for UITableViewCell focusStyle [Alex Hill](https://github.com/alexhillc) [#727](https://github.com/TextureGroup/Texture/pull/727) diff --git a/Tests/ASRunLoopQueueTests.m b/Tests/ASRunLoopQueueTests.m index 485911154..2fc9c04b5 100644 --- a/Tests/ASRunLoopQueueTests.m +++ b/Tests/ASRunLoopQueueTests.m @@ -2,9 +2,10 @@ // ASRunLoopQueueTests.m // Texture // -// Copyright (c) through the present, +// Copyright (c) 2017-present, // Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 //