From e8b8fd651eafbba3836113ca67e48e00b5a932d2 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 20 Jun 2018 17:00:09 -0700 Subject: [PATCH 1/6] Adds support for having multiple interface state delegates. Hopefully in a performant way. --- Source/ASDisplayNode+InterfaceState.h | 119 ++++++++++++++ Source/ASDisplayNode+InterfaceState.m | 154 ++++++++++++++++++ Source/ASDisplayNode+Subclasses.h | 68 -------- Source/ASDisplayNode.h | 50 +++--- Source/ASDisplayNode.mm | 40 +++-- Source/ASNodeController+Beta.m | 7 +- .../Private/ASDisplayNode+FrameworkPrivate.h | 3 - Source/Private/ASDisplayNodeInternal.h | 2 + 8 files changed, 329 insertions(+), 114 deletions(-) create mode 100644 Source/ASDisplayNode+InterfaceState.h create mode 100644 Source/ASDisplayNode+InterfaceState.m diff --git a/Source/ASDisplayNode+InterfaceState.h b/Source/ASDisplayNode+InterfaceState.h new file mode 100644 index 000000000..7553b4f67 --- /dev/null +++ b/Source/ASDisplayNode+InterfaceState.h @@ -0,0 +1,119 @@ +// +// ASDisplayNode+InterfaceState.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 state is available on ASDisplayNode and ASViewController, and + * allows checking whether a node is in an interface situation where it is prudent to trigger certain + * actions: measurement, data loading, display, and visibility (the latter for animations or other onscreen-only effects). + * + * The defualt state, ASInterfaceStateNone, means that the element is not predicted to be onscreen soon and + * preloading should not be performed. Swift: use [] for the default behavior. + */ +typedef NS_OPTIONS(NSUInteger, ASInterfaceState) +{ + /** The element is not predicted to be onscreen soon and preloading should not be performed */ + ASInterfaceStateNone = 0, + /** The element may be added to a view soon that could become visible. Measure the layout, including size calculation. */ + ASInterfaceStateMeasureLayout = 1 << 0, + /** The element is likely enough to come onscreen that disk and/or network data required for display should be fetched. */ + ASInterfaceStatePreload = 1 << 1, + /** The element is very likely to become visible, and concurrent rendering should be executed for any -setNeedsDisplay. */ + ASInterfaceStateDisplay = 1 << 2, + /** The element is physically onscreen by at least 1 pixel. + In practice, all other bit fields should also be set when this flag is set. */ + ASInterfaceStateVisible = 1 << 3, + + /** + * The node is not contained in a cell but it is in a window. + * + * Currently we only set `interfaceState` to other values for + * nodes contained in table views or collection views. + */ + ASInterfaceStateInHierarchy = ASInterfaceStateMeasureLayout | ASInterfaceStatePreload | ASInterfaceStateDisplay | ASInterfaceStateVisible, +}; + +@protocol ASInterfaceStateDelegate +@optional + +/** + * @abstract Called whenever any bit in the ASInterfaceState bitfield is changed. + * @discussion Subclasses may use this to monitor when they become visible, should free cached data, and much more. + * @see ASInterfaceState + */ +- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState; + +/** + * @abstract Called whenever the node becomes visible. + * @discussion Subclasses may use this to monitor when they become visible. + * @note This method is guaranteed to be called on main. + */ +- (void)didEnterVisibleState; + +/** + * @abstract Called whenever the node is no longer visible. + * @discussion Subclasses may use this to monitor when they are no longer visible. + * @note This method is guaranteed to be called on main. + */ +- (void)didExitVisibleState; + +/** + * @abstract Called whenever the the node has entered the display state. + * @discussion Subclasses may use this to monitor when a node should be rendering its content. + * @note This method is guaranteed to be called on main. + */ +- (void)didEnterDisplayState; + +/** + * @abstract Called whenever the the node has exited the display state. + * @discussion Subclasses may use this to monitor when a node should no longer be rendering its content. + * @note This method is guaranteed to be called on main. + */ +- (void)didExitDisplayState; + +/** + * @abstract Called whenever the the node has entered the preload state. + * @discussion Subclasses may use this to monitor data for a node should be preloaded, either from a local or remote source. + * @note This method is guaranteed to be called on main. + */ +- (void)didEnterPreloadState; + +/** + * @abstract Called whenever the the node has exited the preload state. + * @discussion Subclasses may use this to monitor whether preloading data for a node should be canceled. + * @note This method is guaranteed to be called on main. + */ +- (void)didExitPreloadState; + +/** + * @abstract Called when the node has completed applying the layout. + * @discussion Can be used for operations that are performed after layout has completed. + * @note This method is guaranteed to be called on main. + */ +- (void)nodeDidLayout; + +/** + * @abstract Called when the node loads. + * @discussion Can be used for operations that are performed after the node's view is available. + * @note This method is guaranteed to be called on main. + */ +- (void)nodeDidLoad; + +@end + +@interface ASDisplayNodeInterfaceDelegateManager : NSObject + +- (void)addDelegate:(id )delegate; +- (void)removeDelegate:(id )delegate; + +@end diff --git a/Source/ASDisplayNode+InterfaceState.m b/Source/ASDisplayNode+InterfaceState.m new file mode 100644 index 000000000..e7a3f712e --- /dev/null +++ b/Source/ASDisplayNode+InterfaceState.m @@ -0,0 +1,154 @@ +// +// ASDisplayNode+InterfaceState.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 + +@interface ASDisplayNodeInterfaceDelegateManager () +{ + NSHashTable *_interfaceDidChangeDelegates; + NSHashTable *_interfaceDidEnterVisibleDelegates; + NSHashTable *_interfaceDidExitVisibleDelegates; + NSHashTable *_interfaceDidEnterDisplayDelegates; + NSHashTable *_interfaceDidExitDisplayDelegates; + NSHashTable *_interfaceDidEnterPreloadDelegates; + NSHashTable *_interfaceDidExitPreloadDelegates; + NSHashTable *_interfaceNodeDidLayoutDelegates; + NSHashTable *_interfaceNodeDidLoadDelegates; +} +@end + +@implementation ASDisplayNodeInterfaceDelegateManager + +- (instancetype)init +{ + if (self = [super init]) { + _interfaceDidChangeDelegates = [NSHashTable weakObjectsHashTable]; + _interfaceDidEnterVisibleDelegates = [NSHashTable weakObjectsHashTable]; + _interfaceDidExitVisibleDelegates = [NSHashTable weakObjectsHashTable]; + _interfaceDidEnterDisplayDelegates = [NSHashTable weakObjectsHashTable]; + _interfaceDidExitDisplayDelegates = [NSHashTable weakObjectsHashTable]; + _interfaceDidEnterPreloadDelegates = [NSHashTable weakObjectsHashTable]; + _interfaceDidExitPreloadDelegates = [NSHashTable weakObjectsHashTable]; + _interfaceNodeDidLayoutDelegates = [NSHashTable weakObjectsHashTable]; + _interfaceNodeDidLoadDelegates = [NSHashTable weakObjectsHashTable]; + } +} + +- (void)addDelegate:(id)delegate +{ + if ([delegate respondsToSelector:@selector(interfaceStateDidChange:fromState:)]) { + [_interfaceDidChangeDelegates addObject:delegate]; + } + if ([delegate respondsToSelector:@selector(didEnterVisibleState)]) { + [_interfaceDidEnterVisibleDelegates addObject:delegate]; + } + if ([delegate respondsToSelector:@selector(didExitVisibleState)]) { + [_interfaceDidExitVisibleDelegates addObject:delegate]; + } + if ([delegate respondsToSelector:@selector(didEnterDisplayState)]) { + [_interfaceDidEnterDisplayDelegates addObject:delegate]; + } + if ([delegate respondsToSelector:@selector(didExitDisplayState)]) { + [_interfaceDidExitDisplayDelegates addObject:delegate]; + } + if ([delegate respondsToSelector:@selector(didEnterPreloadState)]) { + [_interfaceDidEnterPreloadDelegates addObject:delegate]; + } + if ([delegate respondsToSelector:@selector(didExitPreloadState)]) { + [_interfaceDidExitPreloadDelegates addObject:delegate]; + } + if ([delegate respondsToSelector:@selector(nodeDidLayout)]) { + [_interfaceNodeDidLayoutDelegates addObject:delegate]; + } + if ([delegate respondsToSelector:@selector(nodeDidLoad)]) { + [_interfaceNodeDidLoadDelegates addObject:delegate]; + } +} + +- (void)removeDelegate:(id)delegate +{ + [_interfaceDidChangeDelegates removeObject:delegate]; + [_interfaceDidEnterVisibleDelegates removeObject:delegate]; + [_interfaceDidExitVisibleDelegates removeObject:delegate]; + [_interfaceDidEnterDisplayDelegates removeObject:delegate]; + [_interfaceDidExitDisplayDelegates removeObject:delegate]; + [_interfaceDidEnterPreloadDelegates removeObject:delegate]; + [_interfaceDidExitPreloadDelegates removeObject:delegate]; + [_interfaceNodeDidLayoutDelegates removeObject:delegate]; + [_interfaceNodeDidLoadDelegates removeObject:delegate]; +} + +- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState +{ + for (id delegate in _interfaceDidChangeDelegates) { + [delegate interfaceStateDidChange:newState fromState:oldState]; + } +} + +- (void)didEnterVisibleState +{ + for (id delegate in _interfaceDidEnterVisibleDelegates) { + [delegate didEnterVisibleState]; + } +} + +- (void)didExitVisibleState +{ + for (id delegate in _interfaceDidExitVisibleDelegates) { + [delegate didExitVisibleState]; + } +} + +- (void)didEnterDisplayState +{ + for (id delegate in _interfaceDidEnterDisplayDelegates) { + [delegate didEnterDisplayState]; + } +} + +- (void)didExitDisplayState +{ + for (id delegate in _interfaceDidExitDisplayDelegates) { + [delegate didExitDisplayState]; + } +} + +- (void)didEnterPreloadState +{ + for (id delegate in _interfaceDidEnterPreloadDelegates) { + [delegate didEnterPreloadState]; + } +} + +- (void)didExitPreloadState +{ + for (id delegate in _interfaceDidExitPreloadDelegates) { + [delegate didExitPreloadState]; + } +} + +- (void)nodeDidLayout +{ + for (id delegate in _interfaceNodeDidLayoutDelegates) { + [delegate nodeDidLayout]; + } +} + +- (void)nodeDidLoad +{ + for (id delegate in _interfaceNodeDidLoadDelegates) { + [delegate nodeDidLoad]; + } +} + +@end diff --git a/Source/ASDisplayNode+Subclasses.h b/Source/ASDisplayNode+Subclasses.h index 43f1c235f..d4cbc39f2 100644 --- a/Source/ASDisplayNode+Subclasses.h +++ b/Source/ASDisplayNode+Subclasses.h @@ -42,74 +42,6 @@ NS_ASSUME_NONNULL_BEGIN * variables. */ -@protocol ASInterfaceStateDelegate -@required - -/** - * @abstract Called whenever any bit in the ASInterfaceState bitfield is changed. - * @discussion Subclasses may use this to monitor when they become visible, should free cached data, and much more. - * @see ASInterfaceState - */ -- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState; - -/** - * @abstract Called whenever the node becomes visible. - * @discussion Subclasses may use this to monitor when they become visible. - * @note This method is guaranteed to be called on main. - */ -- (void)didEnterVisibleState; - -/** - * @abstract Called whenever the node is no longer visible. - * @discussion Subclasses may use this to monitor when they are no longer visible. - * @note This method is guaranteed to be called on main. - */ -- (void)didExitVisibleState; - -/** - * @abstract Called whenever the the node has entered the display state. - * @discussion Subclasses may use this to monitor when a node should be rendering its content. - * @note This method is guaranteed to be called on main. - */ -- (void)didEnterDisplayState; - -/** - * @abstract Called whenever the the node has exited the display state. - * @discussion Subclasses may use this to monitor when a node should no longer be rendering its content. - * @note This method is guaranteed to be called on main. - */ -- (void)didExitDisplayState; - -/** - * @abstract Called whenever the the node has entered the preload state. - * @discussion Subclasses may use this to monitor data for a node should be preloaded, either from a local or remote source. - * @note This method is guaranteed to be called on main. - */ -- (void)didEnterPreloadState; - -/** - * @abstract Called whenever the the node has exited the preload state. - * @discussion Subclasses may use this to monitor whether preloading data for a node should be canceled. - * @note This method is guaranteed to be called on main. - */ -- (void)didExitPreloadState; - -/** - * @abstract Called when the node has completed applying the layout. - * @discussion Can be used for operations that are performed after layout has completed. - * @note This method is guaranteed to be called on main. - */ -- (void)nodeDidLayout; - -/** - * @abstract Called when the node loads. - * @discussion Can be used for operations that are performed after the node's view is available. - * @note This method is guaranteed to be called on main. - */ -- (void)nodeDidLoad; - -@end - @interface ASDisplayNode (Subclassing) #pragma mark - Properties diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index e8dcb03c9..4c05ffbac 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -22,6 +22,7 @@ #import #import #import +#import #import #import #import @@ -70,37 +71,6 @@ typedef ASLayoutSpec * _Nonnull(^ASLayoutSpecBlock)(__kindof ASDisplayNode *node */ typedef void (^ASDisplayNodeNonFatalErrorBlock)(NSError *error); -/** - * Interface state is available on ASDisplayNode and ASViewController, and - * allows checking whether a node is in an interface situation where it is prudent to trigger certain - * actions: measurement, data loading, display, and visibility (the latter for animations or other onscreen-only effects). - * - * The defualt state, ASInterfaceStateNone, means that the element is not predicted to be onscreen soon and - * preloading should not be performed. Swift: use [] for the default behavior. - */ -typedef NS_OPTIONS(NSUInteger, ASInterfaceState) -{ - /** The element is not predicted to be onscreen soon and preloading should not be performed */ - ASInterfaceStateNone = 0, - /** The element may be added to a view soon that could become visible. Measure the layout, including size calculation. */ - ASInterfaceStateMeasureLayout = 1 << 0, - /** The element is likely enough to come onscreen that disk and/or network data required for display should be fetched. */ - ASInterfaceStatePreload = 1 << 1, - /** The element is very likely to become visible, and concurrent rendering should be executed for any -setNeedsDisplay. */ - ASInterfaceStateDisplay = 1 << 2, - /** The element is physically onscreen by at least 1 pixel. - In practice, all other bit fields should also be set when this flag is set. */ - ASInterfaceStateVisible = 1 << 3, - - /** - * The node is not contained in a cell but it is in a window. - * - * Currently we only set `interfaceState` to other values for - * nodes contained in table views or collection views. - */ - ASInterfaceStateInHierarchy = ASInterfaceStateMeasureLayout | ASInterfaceStatePreload | ASInterfaceStateDisplay | ASInterfaceStateVisible, -}; - typedef NS_ENUM(NSInteger, ASCornerRoundingType) { ASCornerRoundingTypeDefaultSlowCALayer, ASCornerRoundingTypePrecomposited, @@ -292,6 +262,24 @@ AS_EXTERN NSInteger const ASDefaultDrawingPriority; */ @property (readonly) ASInterfaceState interfaceState; +/** + * @abstract Adds a delegate to recieve notifications on interfaceState changes. + * + * @warning This must be called from the main thread. + * + * @see ASInterfaceState + */ +- (void)addInterfaceStateDelegate:(id )interfaceStateDelegate; + +/** + * @abstract Removes a delegate from recieving notifications on interfaceState changes. + * + * @warning This must be called from the main thread. + * + * @see ASInterfaceState + */ +- (void)removeInterfaceStateDelegate:(id )interfaceStateDelegate; + /** * @abstract Class property that allows to set a block that can be called on non-fatal errors. This * property can be useful for cases when Async Display Kit can recover from an abnormal behavior, but diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index e70552284..34e4ea96f 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -36,6 +36,7 @@ #import #import #import +#import #import #import #import @@ -71,7 +72,6 @@ @protocol CALayerDelegate; @interface ASDisplayNode () - /** * See ASDisplayNodeInternal.h for ivars */ @@ -543,8 +543,8 @@ - (void)_didLoad for (ASDisplayNodeDidLoadBlock block in onDidLoadBlocks) { block(self); } - - [_interfaceStateDelegate nodeDidLoad]; + + [_interfaceStateDelegateManager nodeDidLoad]; } - (void)didLoad @@ -1225,7 +1225,7 @@ - (void)layout ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); ASDisplayNodeAssertTrue(self.isNodeLoaded); - [_interfaceStateDelegate nodeDidLayout]; + [_interfaceStateDelegateManager nodeDidLayout]; } #pragma mark Layout Transition @@ -3142,7 +3142,7 @@ - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfac { // Subclass hook ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegate interfaceStateDidChange:newState fromState:oldState]; + [_interfaceStateDelegateManager interfaceStateDidChange:newState fromState:oldState]; } - (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState @@ -3152,6 +3152,24 @@ - (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfac return willDisplay && (willDisplay != nowDisplay); } +- (void)addInterfaceStateDelegate:(id )interfaceStateDelegate +{ + ASDisplayNodeAssertMainThread(); + + // Not a fan of lazy loading, but this method won't get called very often and avoiding + // the overhead of creating this is probably worth it. + if (_interfaceStateDelegateManager == nil) { + [[ASDisplayNodeInterfaceDelegateManager alloc] init]; + } + [_interfaceStateDelegateManager addDelegate:interfaceStateDelegate]; +} + +- (void)removeInterfaceStateDelegate:(id )interfaceStateDelegate +{ + ASDisplayNodeAssertMainThread(); + [_interfaceStateDelegateManager removeDelegate:interfaceStateDelegate]; +} + - (BOOL)isVisible { ASDN::MutexLocker l(__instanceLock__); @@ -3163,7 +3181,7 @@ - (void)didEnterVisibleState // subclass override ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegate didEnterVisibleState]; + [_interfaceStateDelegateManager didEnterVisibleState]; #if AS_ENABLE_TIPS [ASTipsController.shared nodeDidAppear:self]; #endif @@ -3174,7 +3192,7 @@ - (void)didExitVisibleState // subclass override ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegate didExitVisibleState]; + [_interfaceStateDelegateManager didExitVisibleState]; } - (BOOL)isInDisplayState @@ -3188,7 +3206,7 @@ - (void)didEnterDisplayState // subclass override ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegate didEnterDisplayState]; + [_interfaceStateDelegateManager didEnterDisplayState]; } - (void)didExitDisplayState @@ -3196,7 +3214,7 @@ - (void)didExitDisplayState // subclass override ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegate didExitDisplayState]; + [_interfaceStateDelegateManager didExitDisplayState]; } - (BOOL)isInPreloadState @@ -3247,14 +3265,14 @@ - (void)didEnterPreloadState [self layoutIfNeeded]; } - [_interfaceStateDelegate didEnterPreloadState]; + [_interfaceStateDelegateManager didEnterPreloadState]; } - (void)didExitPreloadState { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegate didExitPreloadState]; + [_interfaceStateDelegateManager didExitPreloadState]; } - (void)clearContents diff --git a/Source/ASNodeController+Beta.m b/Source/ASNodeController+Beta.m index 1bd873e76..9ba0b9608 100644 --- a/Source/ASNodeController+Beta.m +++ b/Source/ASNodeController+Beta.m @@ -47,6 +47,11 @@ - (instancetype)init return self; } +- (void)dealloc +{ + [_weakNode removeInterfaceStateDelegate:self]; +} + - (void)loadNode { self.node = [[ASDisplayNode alloc] init]; @@ -74,7 +79,7 @@ - (void)setupReferencesWithNode:(ASDisplayNode *)node _weakNode = nil; } - node.interfaceStateDelegate = self; + [node addInterfaceStateDelegate:self]; } - (void)setNode:(ASDisplayNode *)node diff --git a/Source/Private/ASDisplayNode+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h index 589d60c22..245d9f382 100644 --- a/Source/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -138,9 +138,6 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarc // Returns the bounds of the node without reaching the view or layer - (CGRect)_locked_threadSafeBounds; -// 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; diff --git a/Source/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h index 18c93f103..ca3e74709 100644 --- a/Source/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -241,6 +241,8 @@ AS_EXTERN NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimest NSTimeInterval _debugTimeToAddSubnodeViews; NSTimeInterval _debugTimeForDidLoad; #endif + + ASDisplayNodeInterfaceDelegateManager *_interfaceStateDelegateManager; } + (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node; From 8c5981e62312265ba679c96c5f3be39a3d1edcc7 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 27 Jun 2018 12:24:12 -0700 Subject: [PATCH 2/6] Switch to respondsToSelector for int del instead of separate object --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 + Source/ASDisplayNode+InterfaceState.h | 17 ++- Source/ASDisplayNode+InterfaceState.m | 154 ---------------------- Source/ASDisplayNode.h | 4 +- Source/ASDisplayNode.mm | 75 +++++++++-- Source/Private/ASDisplayNodeInternal.h | 4 +- 6 files changed, 80 insertions(+), 178 deletions(-) delete mode 100644 Source/ASDisplayNode+InterfaceState.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 4e8a7a19a..6a4f8d32f 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -115,6 +115,7 @@ 68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B361CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m */; }; 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */; }; 68355B411CB57A6C001D4E68 /* ASImageContainerProtocolCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 683F563720E409D700CEB7A3 /* ASDisplayNode+InterfaceState.h in Headers */ = {isa = PBXBuildFile; fileRef = 683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */; }; 68AF37DB1CBEF4D80077BF76 /* ASImageNode+AnimatedImagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; 68B0277B1C1A79D60041016B /* ASDisplayNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; 68B8A4E21CBDB958007E4543 /* ASWeakProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B8A4DF1CBDB958007E4543 /* ASWeakProxy.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -667,6 +668,7 @@ 68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageContainerProtocolCategories.h; sourceTree = ""; }; 68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASImageContainerProtocolCategories.m; sourceTree = ""; }; 68355B391CB57A5A001D4E68 /* ASPINRemoteImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPINRemoteImageDownloader.h; sourceTree = ""; }; + 683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+InterfaceState.h"; sourceTree = ""; }; 68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Beta.h"; sourceTree = ""; }; 68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASImageNode+AnimatedImagePrivate.h"; sourceTree = ""; }; 68B8A4DF1CBDB958007E4543 /* ASWeakProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakProxy.h; sourceTree = ""; }; @@ -1156,6 +1158,7 @@ 68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */, CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */, CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */, + 683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */, 69BCE3D71EC6513B007DCCAD /* ASDisplayNode+Layout.mm */, 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */, 90FC784E1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm */, @@ -1915,6 +1918,7 @@ CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */, 254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */, B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */, + 683F563720E409D700CEB7A3 /* ASDisplayNode+InterfaceState.h in Headers */, 68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */, CCCCCCE11EC3EF060087FE10 /* ASTextUtilities.h in Headers */, B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */, diff --git a/Source/ASDisplayNode+InterfaceState.h b/Source/ASDisplayNode+InterfaceState.h index 7553b4f67..bff9fa9ef 100644 --- a/Source/ASDisplayNode+InterfaceState.h +++ b/Source/ASDisplayNode+InterfaceState.h @@ -109,11 +109,16 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState) */ - (void)nodeDidLoad; -@end - -@interface ASDisplayNodeInterfaceDelegateManager : NSObject - -- (void)addDelegate:(id )delegate; -- (void)removeDelegate:(id )delegate; +/** + * @abstract Indicates that the receiver and all subnodes have finished displaying. + * @discussion May be called more than once, for example if the receiver has a network image node. + * This is called after the first display pass even if network image nodes have not downloaded anything + * (text would be done, and other nodes that are ready to do their final display). Each render of + * every progressive jpeg network node would cause this to be called, so this hook could be called up to + * 1 + (pJPEGcount * pJPEGrenderCount) times. The render count depends on how many times the downloader calls + * the progressImage block. + * @note This method is guaranteed to be called on main. + */ +- (void)hierarchyDisplayDidFinish; @end diff --git a/Source/ASDisplayNode+InterfaceState.m b/Source/ASDisplayNode+InterfaceState.m deleted file mode 100644 index e7a3f712e..000000000 --- a/Source/ASDisplayNode+InterfaceState.m +++ /dev/null @@ -1,154 +0,0 @@ -// -// ASDisplayNode+InterfaceState.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 - -@interface ASDisplayNodeInterfaceDelegateManager () -{ - NSHashTable *_interfaceDidChangeDelegates; - NSHashTable *_interfaceDidEnterVisibleDelegates; - NSHashTable *_interfaceDidExitVisibleDelegates; - NSHashTable *_interfaceDidEnterDisplayDelegates; - NSHashTable *_interfaceDidExitDisplayDelegates; - NSHashTable *_interfaceDidEnterPreloadDelegates; - NSHashTable *_interfaceDidExitPreloadDelegates; - NSHashTable *_interfaceNodeDidLayoutDelegates; - NSHashTable *_interfaceNodeDidLoadDelegates; -} -@end - -@implementation ASDisplayNodeInterfaceDelegateManager - -- (instancetype)init -{ - if (self = [super init]) { - _interfaceDidChangeDelegates = [NSHashTable weakObjectsHashTable]; - _interfaceDidEnterVisibleDelegates = [NSHashTable weakObjectsHashTable]; - _interfaceDidExitVisibleDelegates = [NSHashTable weakObjectsHashTable]; - _interfaceDidEnterDisplayDelegates = [NSHashTable weakObjectsHashTable]; - _interfaceDidExitDisplayDelegates = [NSHashTable weakObjectsHashTable]; - _interfaceDidEnterPreloadDelegates = [NSHashTable weakObjectsHashTable]; - _interfaceDidExitPreloadDelegates = [NSHashTable weakObjectsHashTable]; - _interfaceNodeDidLayoutDelegates = [NSHashTable weakObjectsHashTable]; - _interfaceNodeDidLoadDelegates = [NSHashTable weakObjectsHashTable]; - } -} - -- (void)addDelegate:(id)delegate -{ - if ([delegate respondsToSelector:@selector(interfaceStateDidChange:fromState:)]) { - [_interfaceDidChangeDelegates addObject:delegate]; - } - if ([delegate respondsToSelector:@selector(didEnterVisibleState)]) { - [_interfaceDidEnterVisibleDelegates addObject:delegate]; - } - if ([delegate respondsToSelector:@selector(didExitVisibleState)]) { - [_interfaceDidExitVisibleDelegates addObject:delegate]; - } - if ([delegate respondsToSelector:@selector(didEnterDisplayState)]) { - [_interfaceDidEnterDisplayDelegates addObject:delegate]; - } - if ([delegate respondsToSelector:@selector(didExitDisplayState)]) { - [_interfaceDidExitDisplayDelegates addObject:delegate]; - } - if ([delegate respondsToSelector:@selector(didEnterPreloadState)]) { - [_interfaceDidEnterPreloadDelegates addObject:delegate]; - } - if ([delegate respondsToSelector:@selector(didExitPreloadState)]) { - [_interfaceDidExitPreloadDelegates addObject:delegate]; - } - if ([delegate respondsToSelector:@selector(nodeDidLayout)]) { - [_interfaceNodeDidLayoutDelegates addObject:delegate]; - } - if ([delegate respondsToSelector:@selector(nodeDidLoad)]) { - [_interfaceNodeDidLoadDelegates addObject:delegate]; - } -} - -- (void)removeDelegate:(id)delegate -{ - [_interfaceDidChangeDelegates removeObject:delegate]; - [_interfaceDidEnterVisibleDelegates removeObject:delegate]; - [_interfaceDidExitVisibleDelegates removeObject:delegate]; - [_interfaceDidEnterDisplayDelegates removeObject:delegate]; - [_interfaceDidExitDisplayDelegates removeObject:delegate]; - [_interfaceDidEnterPreloadDelegates removeObject:delegate]; - [_interfaceDidExitPreloadDelegates removeObject:delegate]; - [_interfaceNodeDidLayoutDelegates removeObject:delegate]; - [_interfaceNodeDidLoadDelegates removeObject:delegate]; -} - -- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState -{ - for (id delegate in _interfaceDidChangeDelegates) { - [delegate interfaceStateDidChange:newState fromState:oldState]; - } -} - -- (void)didEnterVisibleState -{ - for (id delegate in _interfaceDidEnterVisibleDelegates) { - [delegate didEnterVisibleState]; - } -} - -- (void)didExitVisibleState -{ - for (id delegate in _interfaceDidExitVisibleDelegates) { - [delegate didExitVisibleState]; - } -} - -- (void)didEnterDisplayState -{ - for (id delegate in _interfaceDidEnterDisplayDelegates) { - [delegate didEnterDisplayState]; - } -} - -- (void)didExitDisplayState -{ - for (id delegate in _interfaceDidExitDisplayDelegates) { - [delegate didExitDisplayState]; - } -} - -- (void)didEnterPreloadState -{ - for (id delegate in _interfaceDidEnterPreloadDelegates) { - [delegate didEnterPreloadState]; - } -} - -- (void)didExitPreloadState -{ - for (id delegate in _interfaceDidExitPreloadDelegates) { - [delegate didExitPreloadState]; - } -} - -- (void)nodeDidLayout -{ - for (id delegate in _interfaceNodeDidLayoutDelegates) { - [delegate nodeDidLayout]; - } -} - -- (void)nodeDidLoad -{ - for (id delegate in _interfaceNodeDidLoadDelegates) { - [delegate nodeDidLoad]; - } -} - -@end diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index 4c05ffbac..dbe641e9b 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -263,7 +263,7 @@ AS_EXTERN NSInteger const ASDefaultDrawingPriority; @property (readonly) ASInterfaceState interfaceState; /** - * @abstract Adds a delegate to recieve notifications on interfaceState changes. + * @abstract Adds a delegate to receive notifications on interfaceState changes. * * @warning This must be called from the main thread. * @@ -272,7 +272,7 @@ AS_EXTERN NSInteger const ASDefaultDrawingPriority; - (void)addInterfaceStateDelegate:(id )interfaceStateDelegate; /** - * @abstract Removes a delegate from recieving notifications on interfaceState changes. + * @abstract Removes a delegate from receiving notifications on interfaceState changes. * * @warning This must be called from the main thread. * diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 34e4ea96f..36b7b01b0 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -433,6 +433,9 @@ - (void)dealloc // TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway. [self _setSupernode:nil]; + + ASPerformMainThreadDeallocation(&_interfaceStateDelegates); + _interfaceStateDelegates = nil; } #pragma mark - Loading @@ -543,8 +546,12 @@ - (void)_didLoad for (ASDisplayNodeDidLoadBlock block in onDidLoadBlocks) { block(self); } - - [_interfaceStateDelegateManager nodeDidLoad]; + + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(nodeDidLoad)]) { + [delegate nodeDidLoad]; + } + } } - (void)didLoad @@ -1225,7 +1232,11 @@ - (void)layout ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); ASDisplayNodeAssertTrue(self.isNodeLoaded); - [_interfaceStateDelegateManager nodeDidLayout]; + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(nodeDidLayout)]) { + [delegate nodeDidLayout]; + } + } } #pragma mark Layout Transition @@ -1447,6 +1458,13 @@ - (void)_pendingNodeDidDisplay:(ASDisplayNode *)node if (_pendingDisplayNodes.isEmpty) { [self hierarchyDisplayDidFinish]; + + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(hierarchyDisplayDidFinish)]) { + [delegate hierarchyDisplayDidFinish]; + } + } + BOOL placeholderShouldPersist = [self placeholderShouldPersist]; __instanceLock__.lock(); @@ -3142,7 +3160,12 @@ - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfac { // Subclass hook ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegateManager interfaceStateDidChange:newState fromState:oldState]; + ASDisplayNodeAssertMainThread(); + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(interfaceStateDidChange:fromState:)]) { + [delegate interfaceStateDidChange:newState fromState:oldState]; + } + } } - (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState @@ -3158,16 +3181,16 @@ - (void)addInterfaceStateDelegate:(id )interfaceStateD // Not a fan of lazy loading, but this method won't get called very often and avoiding // the overhead of creating this is probably worth it. - if (_interfaceStateDelegateManager == nil) { - [[ASDisplayNodeInterfaceDelegateManager alloc] init]; + if (_interfaceStateDelegates == nil) { + _interfaceStateDelegates = [NSHashTable weakObjectsHashTable]; } - [_interfaceStateDelegateManager addDelegate:interfaceStateDelegate]; + [_interfaceStateDelegates addObject:interfaceStateDelegate]; } - (void)removeInterfaceStateDelegate:(id )interfaceStateDelegate { ASDisplayNodeAssertMainThread(); - [_interfaceStateDelegateManager removeDelegate:interfaceStateDelegate]; + [_interfaceStateDelegates removeObject:interfaceStateDelegate]; } - (BOOL)isVisible @@ -3181,7 +3204,11 @@ - (void)didEnterVisibleState // subclass override ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegateManager didEnterVisibleState]; + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(didEnterVisibleState)]) { + [delegate didEnterVisibleState]; + } + } #if AS_ENABLE_TIPS [ASTipsController.shared nodeDidAppear:self]; #endif @@ -3192,7 +3219,11 @@ - (void)didExitVisibleState // subclass override ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegateManager didExitVisibleState]; + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(didExitVisibleState)]) { + [delegate didExitVisibleState]; + } + } } - (BOOL)isInDisplayState @@ -3206,7 +3237,11 @@ - (void)didEnterDisplayState // subclass override ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegateManager didEnterDisplayState]; + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(didEnterDisplayState)]) { + [delegate didEnterDisplayState]; + } + } } - (void)didExitDisplayState @@ -3214,7 +3249,11 @@ - (void)didExitDisplayState // subclass override ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegateManager didExitDisplayState]; + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(didExitDisplayState)]) { + [delegate didExitDisplayState]; + } + } } - (BOOL)isInPreloadState @@ -3265,14 +3304,22 @@ - (void)didEnterPreloadState [self layoutIfNeeded]; } - [_interfaceStateDelegateManager didEnterPreloadState]; + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(didEnterPreloadState)]) { + [delegate didEnterPreloadState]; + } + } } - (void)didExitPreloadState { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - [_interfaceStateDelegateManager didExitPreloadState]; + for (id delegate in _interfaceStateDelegates) { + if ([delegate respondsToSelector:@selector(didExitPreloadState)]) { + [delegate didExitPreloadState]; + } + } } - (void)clearContents diff --git a/Source/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h index ca3e74709..9146a4f1e 100644 --- a/Source/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -241,8 +241,8 @@ AS_EXTERN NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimest NSTimeInterval _debugTimeToAddSubnodeViews; NSTimeInterval _debugTimeForDidLoad; #endif - - ASDisplayNodeInterfaceDelegateManager *_interfaceStateDelegateManager; + + NSHashTable > *_interfaceStateDelegates; } + (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node; From 205f6d4c3d895215b34ebaf5f8ecd3e2d79d481d Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 27 Jun 2018 12:25:43 -0700 Subject: [PATCH 3/6] Add CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb280cddc..c98cc66e8 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. +- [ASDisplayNode] Adds support for multiple interface state delegates. [Garrett Moon](https://github.com/garrettmoon) [#979](https://github.com/TextureGroup/Texture/pull/979) - [ASDataController] Add capability to renew supplementary views (update map) when size change from zero to non-zero.[Max Wang](https://github.com/wsdwsd0829) [#842](https://github.com/TextureGroup/Texture/pull/842) - Make `ASPerformMainThreadDeallocation` visible in C. [Adlai Holler](https://github.com/Adlai-Holler) - Add snapshot test for astextnode2. [Max Wang](https://github.com/wsdwsd0829) [#935](https://github.com/TextureGroup/Texture/pull/935) From 0dcfaec02392bf1c8bf7cd23c68513b62f617d10 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 27 Jun 2018 12:36:25 -0700 Subject: [PATCH 4/6] Make ASDisplayNode+InterfaceState.h public --- AsyncDisplayKit.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 6a4f8d32f..fa52798d7 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -115,7 +115,7 @@ 68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B361CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m */; }; 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */; }; 68355B411CB57A6C001D4E68 /* ASImageContainerProtocolCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 683F563720E409D700CEB7A3 /* ASDisplayNode+InterfaceState.h in Headers */ = {isa = PBXBuildFile; fileRef = 683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */; }; + 683F563720E409D700CEB7A3 /* ASDisplayNode+InterfaceState.h in Headers */ = {isa = PBXBuildFile; fileRef = 683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */; settings = {ATTRIBUTES = (Public, ); }; }; 68AF37DB1CBEF4D80077BF76 /* ASImageNode+AnimatedImagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; 68B0277B1C1A79D60041016B /* ASDisplayNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; 68B8A4E21CBDB958007E4543 /* ASWeakProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B8A4DF1CBDB958007E4543 /* ASWeakProxy.h */; settings = {ATTRIBUTES = (Private, ); }; }; From 2a1c063ea06295f336abe20f63af74a22068121c Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 27 Jun 2018 14:50:22 -0700 Subject: [PATCH 5/6] Huy's comments --- Source/ASDisplayNode.mm | 3 --- Source/ASNodeController+Beta.m | 7 +++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 36b7b01b0..81ab1cd61 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -433,9 +433,6 @@ - (void)dealloc // TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway. [self _setSupernode:nil]; - - ASPerformMainThreadDeallocation(&_interfaceStateDelegates); - _interfaceStateDelegates = nil; } #pragma mark - Loading diff --git a/Source/ASNodeController+Beta.m b/Source/ASNodeController+Beta.m index 9ba0b9608..b08918819 100644 --- a/Source/ASNodeController+Beta.m +++ b/Source/ASNodeController+Beta.m @@ -15,9 +15,10 @@ // http://www.apache.org/licenses/LICENSE-2.0 // -#import -#import +#import #import +#import +#import #define _node (_shouldInvertStrongReference ? _weakNode : _strongNode) @@ -49,7 +50,9 @@ - (instancetype)init - (void)dealloc { + ASPerformBlockOnMainThread(^{ [_weakNode removeInterfaceStateDelegate:self]; + }); } - (void)loadNode From 700d927e3a396d60903e84119f1f838dfbb741d4 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 27 Jun 2018 15:05:31 -0700 Subject: [PATCH 6/6] Don't even bother removing since it's a weak hash table. --- Source/ASNodeController+Beta.m | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Source/ASNodeController+Beta.m b/Source/ASNodeController+Beta.m index b08918819..5f5fcddf6 100644 --- a/Source/ASNodeController+Beta.m +++ b/Source/ASNodeController+Beta.m @@ -48,13 +48,6 @@ - (instancetype)init return self; } -- (void)dealloc -{ - ASPerformBlockOnMainThread(^{ - [_weakNode removeInterfaceStateDelegate:self]; - }); -} - - (void)loadNode { self.node = [[ASDisplayNode alloc] init];