From 52a34db88475529a40a3fb70c51856ede1cf2545 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Thu, 30 Jun 2016 07:44:00 -0700 Subject: [PATCH 1/9] Add pending state for range mode in ASTableNode and ASCollectionNode --- AsyncDisplayKit/ASCollectionNode.h | 4 +- AsyncDisplayKit/ASCollectionNode.mm | 159 +++++++++++------- AsyncDisplayKit/ASTableNode.h | 2 +- AsyncDisplayKit/ASTableNode.mm | 88 ++++++---- .../Details/ASAbstractLayoutController.mm | 6 +- 5 files changed, 163 insertions(+), 96 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionNode.h b/AsyncDisplayKit/ASCollectionNode.h index b65c79a2..c28eae09 100644 --- a/AsyncDisplayKit/ASCollectionNode.h +++ b/AsyncDisplayKit/ASCollectionNode.h @@ -25,11 +25,11 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout; - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout; +@property (strong, nonatomic, readonly) ASCollectionView *view; + @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; -@property (nonatomic, readonly) ASCollectionView *view; - /** * Tuning parameters for a range type in full mode. * diff --git a/AsyncDisplayKit/ASCollectionNode.mm b/AsyncDisplayKit/ASCollectionNode.mm index 2784c9a9..876429c3 100644 --- a/AsyncDisplayKit/ASCollectionNode.mm +++ b/AsyncDisplayKit/ASCollectionNode.mm @@ -12,52 +12,76 @@ #import "ASCollectionNode.h" #import "ASCollectionInternal.h" -#import "ASCollectionViewLayoutFacilitatorProtocol.h" #import "ASDisplayNode+Subclasses.h" #import "ASEnvironmentInternal.h" #import "ASInternalHelpers.h" #import "ASRangeControllerUpdateRangeProtocol+Beta.h" + #include +#pragma mark - _ASCollectionPendingState + @interface _ASCollectionPendingState : NSObject @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; +@property (assign, nonatomic) ASLayoutRangeMode rangeMode; @end @implementation _ASCollectionPendingState -@end -#if 0 // This is not used yet, but will provide a way to avoid creating the view to set range values. -@implementation _ASCollectionPendingState +- (instancetype)init { - std::vector _tuningParameters; + self = [super init]; + if (self) { + _rangeMode = ASLayoutRangeModeCount; + } + return self; +} +@end + +// TODO: Add support for tuning parameters in the pending state +#if 0 // This is not used yet, but will provide a way to avoid creating the view to set range values. +@implementation _ASCollectionPendingState { + std::vector> _tuningParameters; } - (instancetype)init { - if (!(self = [super init])) { - return nil; + self = [super init]; + if (self) { + _tuningParameters = std::vector> (ASLayoutRangeModeCount, std::vector (ASLayoutRangeTypeCount)); + _rangeMode = ASLayoutRangeModeCount; } - _tuningParameters = std::vector(ASLayoutRangeTypeCount); return self; } - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters"); - return _tuningParameters[rangeType]; + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters"); - ASDisplayNodeAssert(rangeType != ASLayoutRangeTypeVisible, @"Must not set Visible range tuning parameters (always 0, 0)"); - _tuningParameters[rangeType] = tuningParameters; + return [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Requesting a range that is OOB for the configured tuning parameters"); + return _tuningParameters[rangeMode][rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Setting a range that is OOB for the configured tuning parameters"); + _tuningParameters[rangeMode][rangeType] = tuningParameters; } @end #endif +#pragma mark - ASCollectionNode + @interface ASCollectionNode () { ASDN::RecursiveMutex _environmentStateLock; @@ -67,6 +91,8 @@ @implementation ASCollectionNode +#pragma mark Lifecycle + - (instancetype)init { ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); @@ -109,6 +135,8 @@ return nil; } +#pragma mark ASDisplayNode + - (void)didLoad { [super didLoad]; @@ -121,9 +149,39 @@ self.pendingState = nil; view.asyncDelegate = pendingState.delegate; view.asyncDataSource = pendingState.dataSource; + if (pendingState.rangeMode != ASLayoutRangeModeCount) { + [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; + } } } +- (ASCollectionView *)view +{ + return (ASCollectionView *)[super view]; +} + +- (void)clearContents +{ + [super clearContents]; + [self.view clearContents]; +} + +- (void)clearFetchedData +{ + [super clearFetchedData]; + [self.view clearFetchedData]; +} + +#if ASRangeControllerLoggingEnabled +- (void)visibleStateDidChange:(BOOL)isVisible +{ + [super visibleStateDidChange:isVisible]; + NSLog(@"%@ - visible: %d", self, isVisible); +} +#endif + +#pragma mark Setter / Getter + - (_ASCollectionPendingState *)pendingState { if (!_pendingState && ![self isNodeLoaded]) { @@ -171,47 +229,7 @@ } } -- (ASCollectionView *)view -{ - return (ASCollectionView *)[super view]; -} - -#if ASRangeControllerLoggingEnabled -- (void)visibleStateDidChange:(BOOL)isVisible -{ - [super visibleStateDidChange:isVisible]; - NSLog(@"%@ - visible: %d", self, isVisible); -} -#endif - -- (void)clearContents -{ - [super clearContents]; - [self.view clearContents]; -} - -- (void)clearFetchedData -{ - [super clearFetchedData]; - [self.view clearFetchedData]; -} - -- (void)beginUpdates -{ - [self.view.dataController beginUpdates]; -} - -- (void)endUpdatesAnimated:(BOOL)animated -{ - [self endUpdatesAnimated:animated completion:nil]; -} - -- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion -{ - [self.view.dataController endUpdatesAnimated:animated completion:completion]; -} - -#pragma mark - ASCollectionView Forwards +#pragma mark ASCollectionView Forwards - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { @@ -233,11 +251,6 @@ return [self.view.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; } -- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode; -{ - [self.view.rangeController updateCurrentRangeWithMode:rangeMode]; -} - - (void)reloadDataWithCompletion:(void (^)())completion { [self.view reloadDataWithCompletion:completion]; @@ -253,6 +266,34 @@ [self.view reloadDataImmediately]; } +- (void)beginUpdates +{ + [self.view.dataController beginUpdates]; +} + +- (void)endUpdatesAnimated:(BOOL)animated +{ + [self endUpdatesAnimated:animated completion:nil]; +} + +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion +{ + [self.view.dataController endUpdatesAnimated:animated completion:completion]; +} + +#pragma mark - ASRangeControllerUpdateRangeProtocol + +- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode; +{ + if ([self pendingState]) { + _pendingState.rangeMode = rangeMode; + } else { + [self.view.rangeController updateCurrentRangeWithMode:rangeMode]; + } +} + +#pragma mark ASEnvironment + ASEnvironmentCollectionTableSetEnvironmentState(_environmentStateLock) @end diff --git a/AsyncDisplayKit/ASTableNode.h b/AsyncDisplayKit/ASTableNode.h index 795e2ef9..4139f36f 100644 --- a/AsyncDisplayKit/ASTableNode.h +++ b/AsyncDisplayKit/ASTableNode.h @@ -21,7 +21,7 @@ - (instancetype)init; // UITableViewStylePlain - (instancetype)initWithStyle:(UITableViewStyle)style; -@property (nonatomic, readonly) ASTableView *view; +@property (strong, nonatomic, readonly) ASTableView *view; // These properties can be set without triggering the view to be created, so it's fine to set them in -init. @property (weak, nonatomic) id delegate; diff --git a/AsyncDisplayKit/ASTableNode.mm b/AsyncDisplayKit/ASTableNode.mm index e5b09fea..191eba63 100644 --- a/AsyncDisplayKit/ASTableNode.mm +++ b/AsyncDisplayKit/ASTableNode.mm @@ -10,21 +10,35 @@ // of patent rights can be found in the PATENTS file in the same directory. // +#import "ASTableNode.h" +#import "ASTableViewInternal.h" #import "ASEnvironmentInternal.h" #import "ASDisplayNode+Subclasses.h" -#import "ASFlowLayoutController.h" #import "ASInternalHelpers.h" #import "ASRangeControllerUpdateRangeProtocol+Beta.h" -#import "ASTableViewInternal.h" + +#pragma mark - _ASTablePendingState @interface _ASTablePendingState : NSObject @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; +@property (assign, nonatomic) ASLayoutRangeMode rangeMode; @end @implementation _ASTablePendingState +- (instancetype)init +{ + self = [super init]; + if (self) { + _rangeMode = ASLayoutRangeModeCount; + } + return self; +} + @end +#pragma mark - ASTableView + @interface ASTableNode () { ASDN::RecursiveMutex _environmentStateLock; @@ -39,6 +53,8 @@ @implementation ASTableNode +#pragma mark Lifecycle + - (instancetype)_initWithTableView:(ASTableView *)tableView { // Avoid a retain cycle. In this case, the ASTableView is creating us, and strongly retains us. @@ -72,6 +88,8 @@ return [self _initWithFrame:CGRectZero style:UITableViewStylePlain dataControllerClass:nil]; } +#pragma mark ASDisplayNode + - (void)didLoad { [super didLoad]; @@ -84,22 +102,43 @@ self.pendingState = nil; view.asyncDelegate = pendingState.delegate; view.asyncDataSource = pendingState.dataSource; + if (pendingState.rangeMode != ASLayoutRangeModeCount) { + [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; + } } } -- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode +- (ASTableView *)view { - if (!self.isNodeLoaded) { - return; - } - - [self.view.rangeController updateCurrentRangeWithMode:rangeMode]; + return (ASTableView *)[super view]; } +- (void)clearContents +{ + [super clearContents]; + [self.view clearContents]; +} + +- (void)clearFetchedData +{ + [super clearFetchedData]; + [self.view clearFetchedData]; +} + +#if ASRangeControllerLoggingEnabled +- (void)visibleStateDidChange:(BOOL)isVisible +{ + [super visibleStateDidChange:isVisible]; + NSLog(@"%@ - visible: %d", self, isVisible); +} +#endif + +#pragma mark Setter / Getter + - (_ASTablePendingState *)pendingState { if (!_pendingState && ![self isNodeLoaded]) { - self.pendingState = [[_ASTablePendingState alloc] init]; + _pendingState = [[_ASTablePendingState alloc] init]; } ASDisplayNodeAssert(![self isNodeLoaded] || !_pendingState, @"ASTableNode should not have a pendingState once it is loaded"); return _pendingState; @@ -143,30 +182,19 @@ } } -- (ASTableView *)view +#pragma mark ASRangeControllerUpdateRangeProtocol + +- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode { - return (ASTableView *)[super view]; + if ([self pendingState]) { + _pendingState.rangeMode = rangeMode; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + [self.view.rangeController updateCurrentRangeWithMode:rangeMode]; + } } -#if ASRangeControllerLoggingEnabled -- (void)visibleStateDidChange:(BOOL)isVisible -{ - [super visibleStateDidChange:isVisible]; - NSLog(@"%@ - visible: %d", self, isVisible); -} -#endif - -- (void)clearContents -{ - [super clearContents]; - [self.view clearContents]; -} - -- (void)clearFetchedData -{ - [super clearFetchedData]; - [self.view clearFetchedData]; -} +#pragma mark ASEnvironment ASEnvironmentCollectionTableSetEnvironmentState(_environmentStateLock) diff --git a/AsyncDisplayKit/Details/ASAbstractLayoutController.mm b/AsyncDisplayKit/Details/ASAbstractLayoutController.mm index 91121083..b0ef11b3 100644 --- a/AsyncDisplayKit/Details/ASAbstractLayoutController.mm +++ b/AsyncDisplayKit/Details/ASAbstractLayoutController.mm @@ -91,15 +91,13 @@ extern BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningPar - (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), - @"Requesting a range that is OOB for the configured tuning parameters"); + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Requesting a range that is OOB for the configured tuning parameters"); return _tuningParameters[rangeMode][rangeType]; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), - @"Setting a range that is OOB for the configured tuning parameters"); + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Setting a range that is OOB for the configured tuning parameters"); _tuningParameters[rangeMode][rangeType] = tuningParameters; } From 637c4f3a9ff52eb59623592ce5b20e7c48ee0c25 Mon Sep 17 00:00:00 2001 From: ricky Date: Thu, 7 Jul 2016 09:23:37 -0700 Subject: [PATCH 2/9] [ASTraitCollection] Remove traitCollectionContext from ASTraitCollection; add containerWindowSize Passing around a pointer was leading to crashes as the ASVC was the sole owner of the context. There are cases where the VC would dealloc while its subnodes were laying out. This could lead to the subnodes accessing a garbage pointer. --- AsyncDisplayKit/ASViewController.h | 12 ------ AsyncDisplayKit/ASViewController.mm | 39 +++---------------- AsyncDisplayKit/Details/ASEnvironment.h | 25 ++---------- AsyncDisplayKit/Details/ASEnvironment.mm | 16 +------- AsyncDisplayKit/Details/ASTraitCollection.h | 16 +------- AsyncDisplayKit/Details/ASTraitCollection.m | 30 +++++++------- .../Private/ASEnvironmentInternal.mm | 2 +- 7 files changed, 28 insertions(+), 112 deletions(-) diff --git a/AsyncDisplayKit/ASViewController.h b/AsyncDisplayKit/ASViewController.h index 6222af81..8ef9bc80 100644 --- a/AsyncDisplayKit/ASViewController.h +++ b/AsyncDisplayKit/ASViewController.h @@ -27,18 +27,6 @@ typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitWindowSizeBlock)(C @property (nonatomic, strong, readonly) DisplayNodeType node; -/** - * An optional context to pass along with an ASTraitCollection. - * This can be used to pass any internal state to all subnodes via the ASTraitCollection that is not - * included in UITraitCollection. This could range from more fine-tuned size classes to a class of - * constants that is based upon the new trait collection. - * - * Be aware that internally this context is held by a C struct which cannot retain the pointer. Therefore - * ASVC keeps a strong reference to the context to make sure that it stays alive. If you change this value - * it will propagate the change to the subnodes. - */ -@property (nonatomic, strong) id _Nullable traitCollectionContext; - /** * Set this block to customize the ASDisplayTraits returned when the VC transitions to the given traitCollection. */ diff --git a/AsyncDisplayKit/ASViewController.mm b/AsyncDisplayKit/ASViewController.mm index 86417d08..deffec60 100644 --- a/AsyncDisplayKit/ASViewController.mm +++ b/AsyncDisplayKit/ASViewController.mm @@ -58,17 +58,6 @@ return self; } -- (void)dealloc -{ - if (_traitCollectionContext != nil) { - // The setter will iterate through the VC's subnodes and replace the traitCollectionContext in their ASEnvironmentTraitCollection with nil. - // Since the VC holds the only strong reference to this context and we are in the process of destroying - // the VC, all the references in the subnodes will be unsafe unless we nil them out. More than likely all the subnodes will be dealloc'ed - // as part of the VC being dealloc'ed, but this is just to make extra sure. - self.traitCollectionContext = nil; - } -} - - (void)loadView { ASDisplayNodeAssertTrue(!_node.layerBacked); @@ -199,27 +188,15 @@ ASVisibilityDepthImplementation; #pragma mark - ASEnvironmentTraitCollection -- (void)setTraitCollectionContext:(id)traitCollectionContext -{ - if (_traitCollectionContext != traitCollectionContext) { - // nil out the displayContext in the subnodes so they aren't hanging around with a dealloc'ed pointer don't set - // the new context yet as this will cause ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection to fail - ASEnvironmentTraitCollectionUpdateDisplayContext(self.node, nil); - - _traitCollectionContext = traitCollectionContext; - } -} - - (ASEnvironmentTraitCollection)environmentTraitCollectionForUITraitCollection:(UITraitCollection *)traitCollection { if (self.overrideDisplayTraitsWithTraitCollection) { ASTraitCollection *asyncTraitCollection = self.overrideDisplayTraitsWithTraitCollection(traitCollection); - self.traitCollectionContext = asyncTraitCollection.traitCollectionContext; return [asyncTraitCollection environmentTraitCollection]; } ASEnvironmentTraitCollection asyncTraitCollection = ASEnvironmentTraitCollectionFromUITraitCollection(traitCollection); - asyncTraitCollection.displayContext = self.traitCollectionContext; + asyncTraitCollection.containerWindowSize = self.view.frame.size; return asyncTraitCollection; } @@ -227,9 +204,9 @@ ASVisibilityDepthImplementation; { if (self.overrideDisplayTraitsWithWindowSize) { ASTraitCollection *traitCollection = self.overrideDisplayTraitsWithWindowSize(windowSize); - self.traitCollectionContext = traitCollection.traitCollectionContext; return [traitCollection environmentTraitCollection]; } + self.node.environmentTraitCollection.containerWindowSize = windowSize; return self.node.environmentTraitCollection; } @@ -255,17 +232,13 @@ ASVisibilityDepthImplementation; [super traitCollectionDidChange:previousTraitCollection]; ASEnvironmentTraitCollection environmentTraitCollection = [self environmentTraitCollectionForUITraitCollection:self.traitCollection]; + environmentTraitCollection.containerWindowSize = self.view.bounds.size; [self progagateNewEnvironmentTraitCollection:environmentTraitCollection]; } -- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id)coordinator -{ - [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator]; - - ASEnvironmentTraitCollection environmentTraitCollection = [self environmentTraitCollectionForUITraitCollection:newCollection]; - [self progagateNewEnvironmentTraitCollection:environmentTraitCollection]; -} - +// Note: We don't override willTransitionToTraitCollection:withTransitionCoordinator: because viewWillTransitionToSize:withTransitionCoordinator: will also called +// called in all these cases. However, there are cases where viewWillTransitionToSize:withTransitionCoordinator: but willTransitionToTraitCollection:withTransitionCoordinator: +// is not. - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; diff --git a/AsyncDisplayKit/Details/ASEnvironment.h b/AsyncDisplayKit/Details/ASEnvironment.h index dd95c2e1..de9902a6 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.h +++ b/AsyncDisplayKit/Details/ASEnvironment.h @@ -69,25 +69,9 @@ typedef struct ASEnvironmentTraitCollection { UIUserInterfaceIdiom userInterfaceIdiom; UIUserInterfaceSizeClass verticalSizeClass; UIForceTouchCapability forceTouchCapability; - - // WARNING: - // This pointer is in a C struct and therefore not managed by ARC. It is - // an unsafe unretained pointer, so when you dereference it you better be - // sure that it is valid. - // - // Use displayContext when you wish to pass view context specific data along with the - // display traits to subnodes. This should be a piece of data owned by an - // ASViewController, which will ensure that the data is still valid when laying out - // its subviews. When the VC is dealloc'ed, the displayContext it created will also - // be dealloced but any subnodes that are hanging around (why would they be?) will now - // have a displayContext that points to a bad pointer. - // - // As an added precaution ASDisplayTraitsClearDisplayContext is called from ASVC's desctructor - // which will propagate a nil displayContext to its subnodes. - id __unsafe_unretained displayContext; -} ASEnvironmentTraitCollection; -extern void ASEnvironmentTraitCollectionUpdateDisplayContext(id rootEnvironment, id _Nullable context); + CGSize containerWindowSize; +} ASEnvironmentTraitCollection; extern ASEnvironmentTraitCollection ASEnvironmentTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection); extern BOOL ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(ASEnvironmentTraitCollection lhs, ASEnvironmentTraitCollection rhs); @@ -165,14 +149,11 @@ ASDISPLAYNODE_EXTERN_C_END if (ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(currentTraits, oldTraits) == NO) {\ /* Must dispatch to main for self.view && [self.view.dataController completedNodes]*/ \ ASPerformBlockOnMainThread(^{\ - BOOL needsLayout = (oldTraits.displayContext == currentTraits.displayContext) || currentTraits.displayContext != nil;\ NSArray *> *completedNodes = [self.view.dataController completedNodes];\ for (NSArray *sectionArray in completedNodes) {\ for (ASCellNode *cellNode in sectionArray) {\ ASEnvironmentStatePropagateDown(cellNode, currentTraits);\ - if (needsLayout) {\ - [cellNode setNeedsLayout];\ - }\ + [cellNode setNeedsLayout];\ }\ }\ });\ diff --git a/AsyncDisplayKit/Details/ASEnvironment.mm b/AsyncDisplayKit/Details/ASEnvironment.mm index 77ffca7a..1f7526d7 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.mm +++ b/AsyncDisplayKit/Details/ASEnvironment.mm @@ -26,23 +26,11 @@ ASEnvironmentHierarchyState _ASEnvironmentHierarchyStateMakeDefault() }; } -extern void ASEnvironmentTraitCollectionUpdateDisplayContext(id rootEnvironment, id context) -{ - ASEnvironmentState envState = [rootEnvironment environmentState]; - ASEnvironmentTraitCollection environmentTraitCollection = envState.environmentTraitCollection; - environmentTraitCollection.displayContext = context; - envState.environmentTraitCollection = environmentTraitCollection; - [rootEnvironment setEnvironmentState:envState]; - - for (id child in [rootEnvironment children]) { - ASEnvironmentStatePropagateDown(child, environmentTraitCollection); - } -} - ASEnvironmentTraitCollection _ASEnvironmentTraitCollectionMakeDefault() { return (ASEnvironmentTraitCollection) { // Default values can be defined in here + .containerWindowSize = CGSizeZero, }; } @@ -69,7 +57,7 @@ BOOL ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(ASEnviron lhs.displayScale == rhs.displayScale && lhs.userInterfaceIdiom == rhs.userInterfaceIdiom && lhs.forceTouchCapability == rhs.forceTouchCapability && - lhs.displayContext == rhs.displayContext; + CGSizeEqualToSize(lhs.containerWindowSize, rhs.containerWindowSize); } ASEnvironmentState ASEnvironmentStateMakeDefault() diff --git a/AsyncDisplayKit/Details/ASTraitCollection.h b/AsyncDisplayKit/Details/ASTraitCollection.h index 9524886d..c737b5d8 100644 --- a/AsyncDisplayKit/Details/ASTraitCollection.h +++ b/AsyncDisplayKit/Details/ASTraitCollection.h @@ -18,21 +18,7 @@ @property (nonatomic, assign, readonly) UIUserInterfaceIdiom userInterfaceIdiom; @property (nonatomic, assign, readonly) UIUserInterfaceSizeClass verticalSizeClass; @property (nonatomic, assign, readonly) UIForceTouchCapability forceTouchCapability; - -/** - * An optional context to pass along with an ASTraitCollection. - * This can be used to pass any internal state to all subnodes via the ASTraitCollection that is not - * included in UITraitCollection. This could range from more fine-tuned size classes to a class of - * constants that is based upon the new trait collection. - * - * Be aware that internally this context is held by a C struct which cannot retain the pointer. - * ASTraitCollection is generally a very short-lived class, existing only to provide a non-struct API - * to trait collections. When an ASTraitCollection is returned via one of ASViewController's 2 - * custom trait collection creation blocks, traitCollectionContext is assigned to the VC's traitCollectionContext. - * This makes sure that the VC is the owner of the context and ASEnvironmentTraitCollections will not - * have a reference to a dangling pointer. - */ -@property (nonatomic, strong, readonly) id traitCollectionContext; +@property (nonatomic, assign, readonly) CGSize containerWindowSize; + (ASTraitCollection *)traitCollectionWithASEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traits; diff --git a/AsyncDisplayKit/Details/ASTraitCollection.m b/AsyncDisplayKit/Details/ASTraitCollection.m index 49fb94c7..f823fde8 100644 --- a/AsyncDisplayKit/Details/ASTraitCollection.m +++ b/AsyncDisplayKit/Details/ASTraitCollection.m @@ -21,7 +21,7 @@ horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - traitCollectionContext:(id)traitCollectionContext + containerWindowSize:(CGSize)windowSize { self = [super init]; if (self) { @@ -30,7 +30,7 @@ _horizontalSizeClass = horizontalSizeClass; _verticalSizeClass = verticalSizeClass; _forceTouchCapability = forceTouchCapability; - _traitCollectionContext = traitCollectionContext; + _containerWindowSize = windowSize; } return self; } @@ -40,29 +40,29 @@ horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - traitCollectionContext:(id)traitCollectionContext + containerWindowSize:(CGSize)windowSize { return [[[self class] alloc] initWithDisplayScale:displayScale userInterfaceIdiom:userInterfaceIdiom horizontalSizeClass:horizontalSizeClass verticalSizeClass:verticalSizeClass forceTouchCapability:forceTouchCapability - traitCollectionContext:traitCollectionContext]; + containerWindowSize:windowSize]; } + (ASTraitCollection *)traitCollectionWithASEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traits { - return [[[self class] alloc] initWithDisplayScale:traits.displayScale - userInterfaceIdiom:traits.userInterfaceIdiom - horizontalSizeClass:traits.horizontalSizeClass - verticalSizeClass:traits.verticalSizeClass - forceTouchCapability:traits.forceTouchCapability - traitCollectionContext:traits.displayContext]; + return [[[self class] alloc] initWithDisplayScale:traits.displayScale + userInterfaceIdiom:traits.userInterfaceIdiom + horizontalSizeClass:traits.horizontalSizeClass + verticalSizeClass:traits.verticalSizeClass + forceTouchCapability:traits.forceTouchCapability + containerWindowSize:traits.containerWindowSize]; } + (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - traitCollectionContext:(id)traitCollectionContext + containerWindowSize:(CGSize)windowSize { ASTraitCollection *asyncTraitCollection = nil; if (AS_AT_LEAST_IOS9) { @@ -71,7 +71,7 @@ horizontalSizeClass:traitCollection.horizontalSizeClass verticalSizeClass:traitCollection.verticalSizeClass forceTouchCapability:traitCollection.forceTouchCapability - traitCollectionContext:traitCollectionContext]; + containerWindowSize:windowSize]; } else if (AS_AT_LEAST_IOS8) { asyncTraitCollection = [[[self class] alloc] initWithDisplayScale:traitCollection.displayScale @@ -79,7 +79,7 @@ horizontalSizeClass:traitCollection.horizontalSizeClass verticalSizeClass:traitCollection.verticalSizeClass forceTouchCapability:0 - traitCollectionContext:traitCollectionContext]; + containerWindowSize:windowSize]; } else { asyncTraitCollection = [[[self class] alloc] init]; } @@ -95,7 +95,7 @@ .userInterfaceIdiom = self.userInterfaceIdiom, .verticalSizeClass = self.verticalSizeClass, .forceTouchCapability = self.forceTouchCapability, - .displayContext = self.traitCollectionContext, + .containerWindowSize = self.containerWindowSize, }; } @@ -105,7 +105,7 @@ self.horizontalSizeClass == traitCollection.horizontalSizeClass && self.verticalSizeClass == traitCollection.verticalSizeClass && self.userInterfaceIdiom == traitCollection.userInterfaceIdiom && - self.traitCollectionContext == traitCollection.traitCollectionContext && + CGSizeEqualToSize(self.containerWindowSize, traitCollection.containerWindowSize) && self.forceTouchCapability == traitCollection.forceTouchCapability; } diff --git a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm index 727b073b..9e80f609 100644 --- a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm +++ b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm @@ -207,7 +207,7 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState childEnvi childTraitCollection.userInterfaceIdiom = parentTraitCollection.userInterfaceIdiom; childTraitCollection.forceTouchCapability = parentTraitCollection.forceTouchCapability; childTraitCollection.displayScale = parentTraitCollection.displayScale; - childTraitCollection.displayContext = parentTraitCollection.displayContext; + childTraitCollection.containerWindowSize = parentTraitCollection.containerWindowSize; childEnvironmentState.environmentTraitCollection = childTraitCollection; } From ec4b666bc54a2734ca86ea1f2cf612137c246010 Mon Sep 17 00:00:00 2001 From: ricky Date: Thu, 7 Jul 2016 09:54:03 -0700 Subject: [PATCH 3/9] update ASTraitCollection header --- AsyncDisplayKit/Details/ASTraitCollection.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASTraitCollection.h b/AsyncDisplayKit/Details/ASTraitCollection.h index c737b5d8..2f125c14 100644 --- a/AsyncDisplayKit/Details/ASTraitCollection.h +++ b/AsyncDisplayKit/Details/ASTraitCollection.h @@ -24,7 +24,7 @@ + (ASTraitCollection *)traitCollectionWithASEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traits; + (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - traitCollectionContext:(id)traitCollectionContext; + containerWindowSize:(CGSize)windowSize; + (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale @@ -32,7 +32,7 @@ horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - traitCollectionContext:(id)traitCollectionContext; + containerWindowSize:(CGSize)windowSize; - (ASEnvironmentTraitCollection)environmentTraitCollection; From 3512cf47aa9b9f509c26528173a85626dccfce7f Mon Sep 17 00:00:00 2001 From: ricky Date: Thu, 7 Jul 2016 10:21:16 -0700 Subject: [PATCH 4/9] assert we are on the main thread before accessing view. --- AsyncDisplayKit/ASViewController.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/AsyncDisplayKit/ASViewController.mm b/AsyncDisplayKit/ASViewController.mm index deffec60..3b0af2d2 100644 --- a/AsyncDisplayKit/ASViewController.mm +++ b/AsyncDisplayKit/ASViewController.mm @@ -195,6 +195,7 @@ ASVisibilityDepthImplementation; return [asyncTraitCollection environmentTraitCollection]; } + ASDisplayNodeAssertMainThread(); ASEnvironmentTraitCollection asyncTraitCollection = ASEnvironmentTraitCollectionFromUITraitCollection(traitCollection); asyncTraitCollection.containerWindowSize = self.view.frame.size; return asyncTraitCollection; From 54ee62f0022496727953fc9413ebc729428c6a53 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Thu, 7 Jul 2016 10:36:41 -0700 Subject: [PATCH 5/9] [ASCollectionView] Add a strong pointer from ASCollectionView -> layer under iOS < 9 to workaround issue --- AsyncDisplayKit/ASCollectionView.mm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index fda250d1..bdcbcd4e 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -107,6 +107,15 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; ASCollectionViewInvalidationStyle _nextLayoutInvalidationStyle; + /** + * Our layer, retained. Under iOS < 9, when collection views are removed from the hierarchy, + * their layers may be deallocated and become dangling pointers. This puts the collection view + * into a very dangerous state where pretty much any call will crash it. So we manually retain our layer. + * + * You should never access this, and it will be nil under iOS >= 9. + */ + CALayer *_retainedLayer; + /** * If YES, the `UICollectionView` will reload its data on next layout pass so we should not forward any updates to it. @@ -248,6 +257,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kCellReuseIdentifier]; + if (!AS_AT_LEAST_IOS9) { + _retainedLayer = self.layer; + } + return self; } From 7314a18ab4915147aa8c3f8a34c7655c1dec7df1 Mon Sep 17 00:00:00 2001 From: ricky Date: Thu, 7 Jul 2016 10:55:18 -0700 Subject: [PATCH 6/9] rename containerWindowSize to containerSize --- AsyncDisplayKit/ASViewController.mm | 6 +++--- AsyncDisplayKit/Details/ASEnvironment.h | 2 +- AsyncDisplayKit/Details/ASEnvironment.mm | 4 ++-- AsyncDisplayKit/Details/ASTraitCollection.h | 6 +++--- AsyncDisplayKit/Details/ASTraitCollection.m | 20 +++++++++---------- .../Private/ASEnvironmentInternal.mm | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/AsyncDisplayKit/ASViewController.mm b/AsyncDisplayKit/ASViewController.mm index 3b0af2d2..4dc56942 100644 --- a/AsyncDisplayKit/ASViewController.mm +++ b/AsyncDisplayKit/ASViewController.mm @@ -197,7 +197,7 @@ ASVisibilityDepthImplementation; ASDisplayNodeAssertMainThread(); ASEnvironmentTraitCollection asyncTraitCollection = ASEnvironmentTraitCollectionFromUITraitCollection(traitCollection); - asyncTraitCollection.containerWindowSize = self.view.frame.size; + asyncTraitCollection.containerSize = self.view.frame.size; return asyncTraitCollection; } @@ -207,7 +207,7 @@ ASVisibilityDepthImplementation; ASTraitCollection *traitCollection = self.overrideDisplayTraitsWithWindowSize(windowSize); return [traitCollection environmentTraitCollection]; } - self.node.environmentTraitCollection.containerWindowSize = windowSize; + self.node.environmentTraitCollection.containerSize = windowSize; return self.node.environmentTraitCollection; } @@ -233,7 +233,7 @@ ASVisibilityDepthImplementation; [super traitCollectionDidChange:previousTraitCollection]; ASEnvironmentTraitCollection environmentTraitCollection = [self environmentTraitCollectionForUITraitCollection:self.traitCollection]; - environmentTraitCollection.containerWindowSize = self.view.bounds.size; + environmentTraitCollection.containerSize = self.view.bounds.size; [self progagateNewEnvironmentTraitCollection:environmentTraitCollection]; } diff --git a/AsyncDisplayKit/Details/ASEnvironment.h b/AsyncDisplayKit/Details/ASEnvironment.h index de9902a6..5cb63315 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.h +++ b/AsyncDisplayKit/Details/ASEnvironment.h @@ -70,7 +70,7 @@ typedef struct ASEnvironmentTraitCollection { UIUserInterfaceSizeClass verticalSizeClass; UIForceTouchCapability forceTouchCapability; - CGSize containerWindowSize; + CGSize containerSize; } ASEnvironmentTraitCollection; extern ASEnvironmentTraitCollection ASEnvironmentTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection); diff --git a/AsyncDisplayKit/Details/ASEnvironment.mm b/AsyncDisplayKit/Details/ASEnvironment.mm index 1f7526d7..7e2edb5b 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.mm +++ b/AsyncDisplayKit/Details/ASEnvironment.mm @@ -30,7 +30,7 @@ ASEnvironmentTraitCollection _ASEnvironmentTraitCollectionMakeDefault() { return (ASEnvironmentTraitCollection) { // Default values can be defined in here - .containerWindowSize = CGSizeZero, + .containerSize = CGSizeZero, }; } @@ -57,7 +57,7 @@ BOOL ASEnvironmentTraitCollectionIsEqualToASEnvironmentTraitCollection(ASEnviron lhs.displayScale == rhs.displayScale && lhs.userInterfaceIdiom == rhs.userInterfaceIdiom && lhs.forceTouchCapability == rhs.forceTouchCapability && - CGSizeEqualToSize(lhs.containerWindowSize, rhs.containerWindowSize); + CGSizeEqualToSize(lhs.containerSize, rhs.containerSize); } ASEnvironmentState ASEnvironmentStateMakeDefault() diff --git a/AsyncDisplayKit/Details/ASTraitCollection.h b/AsyncDisplayKit/Details/ASTraitCollection.h index 2f125c14..19e23131 100644 --- a/AsyncDisplayKit/Details/ASTraitCollection.h +++ b/AsyncDisplayKit/Details/ASTraitCollection.h @@ -18,13 +18,13 @@ @property (nonatomic, assign, readonly) UIUserInterfaceIdiom userInterfaceIdiom; @property (nonatomic, assign, readonly) UIUserInterfaceSizeClass verticalSizeClass; @property (nonatomic, assign, readonly) UIForceTouchCapability forceTouchCapability; -@property (nonatomic, assign, readonly) CGSize containerWindowSize; +@property (nonatomic, assign, readonly) CGSize containerSize; + (ASTraitCollection *)traitCollectionWithASEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traits; + (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerWindowSize:(CGSize)windowSize; + containerSize:(CGSize)windowSize; + (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale @@ -32,7 +32,7 @@ horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - containerWindowSize:(CGSize)windowSize; + containerSize:(CGSize)windowSize; - (ASEnvironmentTraitCollection)environmentTraitCollection; diff --git a/AsyncDisplayKit/Details/ASTraitCollection.m b/AsyncDisplayKit/Details/ASTraitCollection.m index f823fde8..a087c84c 100644 --- a/AsyncDisplayKit/Details/ASTraitCollection.m +++ b/AsyncDisplayKit/Details/ASTraitCollection.m @@ -21,7 +21,7 @@ horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - containerWindowSize:(CGSize)windowSize + containerSize:(CGSize)windowSize { self = [super init]; if (self) { @@ -30,7 +30,7 @@ _horizontalSizeClass = horizontalSizeClass; _verticalSizeClass = verticalSizeClass; _forceTouchCapability = forceTouchCapability; - _containerWindowSize = windowSize; + _containerSize = windowSize; } return self; } @@ -40,14 +40,14 @@ horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - containerWindowSize:(CGSize)windowSize + containerSize:(CGSize)windowSize { return [[[self class] alloc] initWithDisplayScale:displayScale userInterfaceIdiom:userInterfaceIdiom horizontalSizeClass:horizontalSizeClass verticalSizeClass:verticalSizeClass forceTouchCapability:forceTouchCapability - containerWindowSize:windowSize]; + containerSize:windowSize]; } + (ASTraitCollection *)traitCollectionWithASEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traits @@ -57,12 +57,12 @@ horizontalSizeClass:traits.horizontalSizeClass verticalSizeClass:traits.verticalSizeClass forceTouchCapability:traits.forceTouchCapability - containerWindowSize:traits.containerWindowSize]; + containerSize:traits.containerSize]; } + (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerWindowSize:(CGSize)windowSize + containerSize:(CGSize)windowSize { ASTraitCollection *asyncTraitCollection = nil; if (AS_AT_LEAST_IOS9) { @@ -71,7 +71,7 @@ horizontalSizeClass:traitCollection.horizontalSizeClass verticalSizeClass:traitCollection.verticalSizeClass forceTouchCapability:traitCollection.forceTouchCapability - containerWindowSize:windowSize]; + containerSize:windowSize]; } else if (AS_AT_LEAST_IOS8) { asyncTraitCollection = [[[self class] alloc] initWithDisplayScale:traitCollection.displayScale @@ -79,7 +79,7 @@ horizontalSizeClass:traitCollection.horizontalSizeClass verticalSizeClass:traitCollection.verticalSizeClass forceTouchCapability:0 - containerWindowSize:windowSize]; + containerSize:windowSize]; } else { asyncTraitCollection = [[[self class] alloc] init]; } @@ -95,7 +95,7 @@ .userInterfaceIdiom = self.userInterfaceIdiom, .verticalSizeClass = self.verticalSizeClass, .forceTouchCapability = self.forceTouchCapability, - .containerWindowSize = self.containerWindowSize, + .containerSize = self.containerSize, }; } @@ -105,7 +105,7 @@ self.horizontalSizeClass == traitCollection.horizontalSizeClass && self.verticalSizeClass == traitCollection.verticalSizeClass && self.userInterfaceIdiom == traitCollection.userInterfaceIdiom && - CGSizeEqualToSize(self.containerWindowSize, traitCollection.containerWindowSize) && + CGSizeEqualToSize(self.containerSize, traitCollection.containerSize) && self.forceTouchCapability == traitCollection.forceTouchCapability; } diff --git a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm index 9e80f609..5a3c88b7 100644 --- a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm +++ b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm @@ -207,7 +207,7 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState childEnvi childTraitCollection.userInterfaceIdiom = parentTraitCollection.userInterfaceIdiom; childTraitCollection.forceTouchCapability = parentTraitCollection.forceTouchCapability; childTraitCollection.displayScale = parentTraitCollection.displayScale; - childTraitCollection.containerWindowSize = parentTraitCollection.containerWindowSize; + childTraitCollection.containerSize = parentTraitCollection.containerSize; childEnvironmentState.environmentTraitCollection = childTraitCollection; } From ff0e2846e34c3262ea9066615949201be48df113 Mon Sep 17 00:00:00 2001 From: ricky Date: Thu, 7 Jul 2016 10:56:58 -0700 Subject: [PATCH 7/9] fixed formatting --- AsyncDisplayKit/Details/ASTraitCollection.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASTraitCollection.m b/AsyncDisplayKit/Details/ASTraitCollection.m index a087c84c..3bee2582 100644 --- a/AsyncDisplayKit/Details/ASTraitCollection.m +++ b/AsyncDisplayKit/Details/ASTraitCollection.m @@ -57,7 +57,7 @@ horizontalSizeClass:traits.horizontalSizeClass verticalSizeClass:traits.verticalSizeClass forceTouchCapability:traits.forceTouchCapability - containerSize:traits.containerSize]; + containerSize:traits.containerSize]; } From 312de1a0849ce560d7ae885f878af4e9eb37aa70 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 7 Jul 2016 17:41:58 +0700 Subject: [PATCH 8/9] Make sure range controller listens to node display notifications if absolutely needed --- AsyncDisplayKit/ASDisplayNode.mm | 32 +++++++++---------- AsyncDisplayKit/Details/ASRangeController.mm | 29 +++++++++-------- .../Private/ASDisplayNode+FrameworkPrivate.h | 5 +++ 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 32b1c80b..d4ff7e64 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -245,9 +245,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) dispatch_once(&onceToken, ^{ renderQueue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() andHandler:^(ASDisplayNode * _Nonnull dequeuedItem, BOOL isQueueDrained) { - CFAbsoluteTime timestamp = isQueueDrained ? CFAbsoluteTimeGetCurrent() : 0; [dequeuedItem _recursivelyTriggerDisplayAndBlock:NO]; if (isQueueDrained) { + CFAbsoluteTime timestamp = CFAbsoluteTimeGetCurrent(); [[NSNotificationCenter defaultCenter] postNotificationName:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil userInfo:@{ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp: @(timestamp)}]; @@ -2317,27 +2317,27 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) }); } -- (void)recursivelySetInterfaceState:(ASInterfaceState)interfaceState +- (void)recursivelySetInterfaceState:(ASInterfaceState)newInterfaceState { - ASInterfaceState oldState = self.interfaceState; - ASInterfaceState newState = interfaceState; + // Instead of each node in the recursion assuming it needs to schedule itself for display, + // setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set). + // If our range manager intends for us to be displayed right now, and didn't before, get started! + BOOL shouldScheduleDisplay = [self supportsRangeManagedInterfaceState] && [self shouldScheduleDisplayWithNewInterfaceState:newInterfaceState]; ASDisplayNodePerformBlockOnEveryNode(nil, self, ^(ASDisplayNode *node) { - node.interfaceState = interfaceState; + node.interfaceState = newInterfaceState; }); - - if ([self supportsRangeManagedInterfaceState]) { - // Instead of each node in the recursion assuming it needs to schedule itself for display, - // setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set). - // If our range manager intends for us to be displayed right now, and didn't before, get started! - - BOOL nowDisplay = ASInterfaceStateIncludesDisplay(newState); - BOOL wasDisplay = ASInterfaceStateIncludesDisplay(oldState); - if (nowDisplay && (nowDisplay != wasDisplay)) { - [ASDisplayNode scheduleNodeForRecursiveDisplay:self]; - } + if (shouldScheduleDisplay) { + [ASDisplayNode scheduleNodeForRecursiveDisplay:self]; } } +- (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState +{ + BOOL willDisplay = ASInterfaceStateIncludesDisplay(newInterfaceState); + BOOL nowDisplay = ASInterfaceStateIncludesDisplay(self.interfaceState); + return willDisplay && (willDisplay != nowDisplay); +} + - (ASHierarchyState)hierarchyState { ASDN::MutexLocker l(_propertyLock); diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 6c849355..f1d354af 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -27,7 +27,7 @@ NSSet *_allPreviousIndexPaths; ASLayoutRangeMode _currentRangeMode; BOOL _didUpdateCurrentRange; - BOOL _didRegisterForNotifications; + BOOL _didRegisterForNodeDisplayNotifications; CFAbsoluteTime _pendingDisplayNodesTimestamp; } @@ -56,7 +56,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; - (void)dealloc { - if (_didRegisterForNotifications) { + if (_didRegisterForNodeDisplayNotifications) { [[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil]; } } @@ -242,10 +242,6 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; [allIndexPaths addObjectsFromArray:ASIndexPathsForTwoDimensionalArray(allNodes)]; } - // TODO Don't register for notifications if this range update doesn't cause any node to enter rendering pipeline. - // This can be done once there is an API to observe to (or be notified upon) interface state changes or pipeline enterings - [self registerForNotificationsForInterfaceStateIfNeeded:selfInterfaceState]; - #if ASRangeControllerLoggingEnabled ASDisplayNodeAssertTrue([visibleIndexPaths isSubsetOfSet:displayIndexPaths]); NSMutableArray *modifiedIndexPaths = (ASRangeControllerLoggingEnabled ? [NSMutableArray array] : nil); @@ -309,16 +305,21 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; #if ASRangeControllerLoggingEnabled [modifiedIndexPaths addObject:indexPath]; #endif + + BOOL nodeShouldScheduleDisplay = [node shouldScheduleDisplayWithNewInterfaceState:interfaceState]; [node recursivelySetInterfaceState:interfaceState]; + + if (nodeShouldScheduleDisplay) { + [self registerForNodeDisplayNotificationsForInterfaceStateIfNeeded:selfInterfaceState]; + if (_didRegisterForNodeDisplayNotifications) { + _pendingDisplayNodesTimestamp = CFAbsoluteTimeGetCurrent(); + } + } } } } } - if (_didRegisterForNotifications) { - _pendingDisplayNodesTimestamp = CFAbsoluteTimeGetCurrent(); - } - _rangeIsValid = YES; _queuedRangeUpdate = NO; @@ -338,9 +339,9 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; #pragma mark - Notification observers -- (void)registerForNotificationsForInterfaceStateIfNeeded:(ASInterfaceState)interfaceState +- (void)registerForNodeDisplayNotificationsForInterfaceStateIfNeeded:(ASInterfaceState)interfaceState { - if (!_didRegisterForNotifications) { + if (!_didRegisterForNodeDisplayNotifications) { ASLayoutRangeMode nextRangeMode = [ASRangeController rangeModeForInterfaceState:interfaceState currentRangeMode:_currentRangeMode]; if (_currentRangeMode != nextRangeMode) { @@ -348,7 +349,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; selector:@selector(scheduledNodesDidDisplay:) name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil]; - _didRegisterForNotifications = YES; + _didRegisterForNodeDisplayNotifications = YES; } } } @@ -359,7 +360,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; if (_pendingDisplayNodesTimestamp < notificationTimestamp) { // The rendering engine has processed all the nodes this range controller scheduled. Let's schedule a range update [[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil]; - _didRegisterForNotifications = NO; + _didRegisterForNodeDisplayNotifications = NO; [self scheduleRangeUpdate]; } diff --git a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h index 78ce6ce0..30cf597f 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h +++ b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h @@ -135,6 +135,11 @@ inline BOOL ASHierarchyStateIncludesRangeManaged(ASHierarchyState hierarchyState */ @property (nonatomic, assign) BOOL shouldBypassEnsureDisplay; +/** + * @abstract Checks whether a node should be scheduled for display, considering its current and new interface states. + */ +- (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState; + @end @interface UIView (ASDisplayNodeInternal) From a0aad4609d9486a1cbc48044de69d2343100e3a2 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Fri, 8 Jul 2016 11:36:43 -0700 Subject: [PATCH 9/9] [ASDataController] Temporarily disable some troublesome assertions --- AsyncDisplayKit/Details/ASChangeSetDataController.m | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/Details/ASChangeSetDataController.m b/AsyncDisplayKit/Details/ASChangeSetDataController.m index 3a31b8ce..235ad254 100644 --- a/AsyncDisplayKit/Details/ASChangeSetDataController.m +++ b/AsyncDisplayKit/Details/ASChangeSetDataController.m @@ -27,7 +27,8 @@ - (void)beginUpdates { - ASDisplayNodeAssertMainThread(); + // NOTE: This assertion is failing in some apps and will be enabled soon. +// ASDisplayNodeAssertMainThread(); if (_changeSetBatchUpdateCounter <= 0) { _changeSet = [_ASHierarchyChangeSet new]; _changeSetBatchUpdateCounter = 0; @@ -37,11 +38,13 @@ - (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { - ASDisplayNodeAssertMainThread(); + // NOTE: This assertion is failing in some apps and will be enabled soon. +// ASDisplayNodeAssertMainThread(); _changeSetBatchUpdateCounter--; // Prevent calling endUpdatesAnimated:completion: in an unbalanced way - NSAssert(_changeSetBatchUpdateCounter >= 0, @"endUpdatesAnimated:completion: called without having a balanced beginUpdates call"); + // NOTE: This assertion is failing in some apps and will be enabled soon. +// NSAssert(_changeSetBatchUpdateCounter >= 0, @"endUpdatesAnimated:completion: called without having a balanced beginUpdates call"); if (_changeSetBatchUpdateCounter == 0) { [_changeSet markCompleted];