diff --git a/AsyncDisplayKit/ASCollectionNode.mm b/AsyncDisplayKit/ASCollectionNode.mm index de39662d16..faf8314caa 100644 --- a/AsyncDisplayKit/ASCollectionNode.mm +++ b/AsyncDisplayKit/ASCollectionNode.mm @@ -167,10 +167,10 @@ [self.rangeController clearContents]; } -- (void)clearFetchedData +- (void)didExitPreloadState { - [super clearFetchedData]; - [self.rangeController clearFetchedData]; + [super didExitPreloadState]; + [self.rangeController clearPreloadedData]; } - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 7634f5361b..28f0930476 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -1681,18 +1681,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; _nextLayoutInvalidationStyle = invalidationStyle; } -#pragma mark - Memory Management - -- (void)clearContents -{ - [_rangeController clearContents]; -} - -- (void)clearFetchedData -{ - [_rangeController clearFetchedData]; -} - #pragma mark - _ASDisplayView behavior substitutions // Need these to drive interfaceState so we know when we are visible, if not nested in another range-managing element. // Because our superclass is a true UIKit class, we cannot also subclass _ASDisplayView. diff --git a/AsyncDisplayKit/ASDisplayNode+Deprecated.h b/AsyncDisplayKit/ASDisplayNode+Deprecated.h index ec7836d175..a55fbf86a6 100644 --- a/AsyncDisplayKit/ASDisplayNode+Deprecated.h +++ b/AsyncDisplayKit/ASDisplayNode+Deprecated.h @@ -115,4 +115,21 @@ ASLayoutElementStyleForwardingDeclaration */ @property (nonatomic, assign) BOOL usesImplicitHierarchyManagement ASDISPLAYNODE_DEPRECATED_MSG("Set .automaticallyManagesSubnodes instead."); +/** + * @abstract Indicates that the node should fetch any external data, such as images. + * + * @discussion Subclasses may override this method to be notified when they should begin to preload. Fetching + * should be done asynchronously. The node is also responsible for managing the memory of any data. + * The data may be remote and accessed via the network, but could also be a local database query. + */ +- (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterPreloadState instead."); + +/** + * Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node. + * + * @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearPreloadedData or + * selectively clear fetched data. + */ +- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didExitPreloadState instead."); + @end diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index a038661cf0..51ecd2d369 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -323,23 +323,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy; -/** - * @abstract Indicates that the node should fetch any external data, such as images. - * - * @discussion Subclasses may override this method to be notified when they should begin to fetch data. Fetching - * should be done asynchronously. The node is also responsible for managing the memory of any data. - * The data may be remote and accessed via the network, but could also be a local database query. - */ -- (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER; - -/** - * Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node. - * - * @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or - * selectively clear fetched data. - */ -- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER; - /** * Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers * on the current node. diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 289b6246dd..940e2f74fa 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -478,31 +478,6 @@ extern NSInteger const ASDefaultDrawingPriority; */ - (void)recursivelyClearContents; -/** - * @abstract Calls -clearFetchedData on the receiver and its subnode hierarchy. - * - * @discussion Clears any memory-intensive fetched content. - * This method is used to notify the node that it should purge any content that is both expensive to fetch and to - * retain in memory. - * - * @see [ASDisplayNode(Subclassing) clearFetchedData] and [ASDisplayNode(Subclassing) fetchData] - */ -- (void)recursivelyClearFetchedData; - -/** - * @abstract Calls -fetchData on the receiver and its subnode hierarchy. - * - * @discussion Fetches content from remote sources for the current node and all subnodes. - * - * @see [ASDisplayNode(Subclassing) fetchData] and [ASDisplayNode(Subclassing) clearFetchedData] - */ -- (void)recursivelyFetchData; - -/** - * @abstract Triggers a recursive call to fetchData when the node has an interfaceState of ASInterfaceStatePreload - */ -- (void)setNeedsDataFetch; - /** * @abstract Toggle displaying a placeholder over the node that covers content until the node and all subnodes are * displayed. diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index f14d326fb9..01bd984324 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -184,6 +184,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) if (ASDisplayNodeSubclassOverridesSelector(c, @selector(layoutSpecThatFits:))) { overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits; } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(fetchData))) { + overrides |= ASDisplayNodeMethodOverrideFetchData; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(clearFetchedData))) { + overrides |= ASDisplayNodeMethodOverrideClearFetchedData; + } return overrides; } @@ -211,7 +217,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead overwrite calculateLayoutThatFits:.", classString); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead overwrite calculateLayoutThatFits:.", classString); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearPreloadedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString); } // Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values @@ -2930,34 +2936,24 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) }); } -- (void)fetchData -{ - // subclass override -} - -- (void)setNeedsDataFetch +- (void)setNeedsPreload { if (self.isInPreloadState) { - [self recursivelyFetchData]; + [self recursivelyPreload]; } } -- (void)recursivelyFetchData +- (void)recursivelyPreload { ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) { - [node fetchData]; + [node didEnterPreloadState]; }); } -- (void)clearFetchedData -{ - // subclass override -} - -- (void)recursivelyClearFetchedData +- (void)recursivelyClearPreloadedData { ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) { - [node clearFetchedData]; + [node didExitPreloadState]; }); } @@ -2983,13 +2979,23 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) - (void)didEnterPreloadState { - [self fetchData]; + if (_methodOverrides & ASDisplayNodeMethodOverrideFetchData) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [self fetchData]; +#pragma clang diagnostic pop + } } - (void)didExitPreloadState { - if ([self supportsRangeManagedInterfaceState]) { + if (_methodOverrides & ASDisplayNodeMethodOverrideClearFetchedData) { + if ([self supportsRangeManagedInterfaceState]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self clearFetchedData]; +#pragma clang diagnostic pop + } } } @@ -3047,7 +3053,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) // Trigger asynchronous measurement if it is not already cached or being calculated. } - // For the FetchData and Display ranges, we don't want to call -clear* if not being managed by a range controller. + // For the Preload and Display ranges, we don't want to call -clear* if not being managed by a range controller. // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop. // Still, the interfaceState should be updated to the current state of the node; just don't act on the transition. @@ -3062,7 +3068,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) [self didExitPreloadState]; } } - + // Entered or exited contents rendering state. BOOL nowDisplay = ASInterfaceStateIncludesDisplay(newState); BOOL wasDisplay = ASInterfaceStateIncludesDisplay(oldState); @@ -3946,6 +3952,16 @@ ASLayoutElementStyleForwarding } } +- (void)fetchData +{ + // subclass override +} + +- (void)clearFetchedData +{ + // subclass override +} + - (void)cancelLayoutTransitionsInProgress { [self cancelLayoutTransition]; diff --git a/AsyncDisplayKit/ASMapNode.mm b/AsyncDisplayKit/ASMapNode.mm index d188223159..111f52488d 100644 --- a/AsyncDisplayKit/ASMapNode.mm +++ b/AsyncDisplayKit/ASMapNode.mm @@ -72,9 +72,9 @@ [super setLayerBacked:layerBacked]; } -- (void)fetchData +- (void)didEnterPreloadState { - [super fetchData]; + [super didEnterPreloadState]; ASPerformBlockOnMainThread(^{ if (self.isLiveMap) { [self addLiveMap]; @@ -84,9 +84,9 @@ }); } -- (void)clearFetchedData +- (void)didExitPreloadState { - [super clearFetchedData]; + [super didExitPreloadState]; ASPerformBlockOnMainThread(^{ if (self.isLiveMap) { [self removeLiveMap]; diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/AsyncDisplayKit/ASMultiplexImageNode.mm index 011071fe62..f5bc472509 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.mm +++ b/AsyncDisplayKit/ASMultiplexImageNode.mm @@ -216,12 +216,12 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent [super clearContents]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful. [self _setDisplayedImageIdentifier:nil withImage:nil]; - // NOTE: We intentionally do not cancel image downloads until `clearFetchedData`. + // NOTE: We intentionally do not cancel image downloads until `clearPreloadedData`. } -- (void)clearFetchedData +- (void)didExitPreloadState { - [super clearFetchedData]; + [super didExitPreloadState]; [_phImageRequestOperation cancel]; @@ -236,9 +236,9 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent self.image = nil; } -- (void)fetchData +- (void)didEnterPreloadState { - [super fetchData]; + [super didEnterPreloadState]; [self _loadImageIdentifiers]; } @@ -281,7 +281,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent { [super displayWillStart]; - [self fetchData]; + [self didEnterPreloadState]; if (_downloaderImplementsSetPriority) { { @@ -396,7 +396,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent _imageIdentifiers = [[NSArray alloc] initWithArray:imageIdentifiers copyItems:YES]; } - [self setNeedsDataFetch]; + [self setNeedsPreload]; } - (void)reloadImageIdentifierSources diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index 8a989c5c8d..bb106aa4e6 100755 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -145,7 +145,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; }); } - [self setNeedsDataFetch]; + [self setNeedsPreload]; } - (NSURL *)URL @@ -265,8 +265,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; } } - // TODO: Consider removing this; it predates ASInterfaceState, which now ensures that even non-range-managed nodes get a -fetchData call. - [self fetchData]; + // TODO: Consider removing this; it predates ASInterfaceState, which now ensures that even non-range-managed nodes get a -preload call. + [self didEnterPreloadState]; if (self.image == nil && _downloaderFlags.downloaderImplementsSetPriority) { ASDN::MutexLocker l(__instanceLock__); @@ -306,9 +306,9 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; [self _updateProgressImageBlockOnDownloaderIfNeeded]; } -- (void)clearFetchedData +- (void)didExitPreloadState { - [super clearFetchedData]; + [super didExitPreloadState]; { ASDN::MutexLocker l(__instanceLock__); @@ -321,9 +321,9 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; } } -- (void)fetchData +- (void)didEnterPreloadState { - [super fetchData]; + [super didEnterPreloadState]; { ASDN::MutexLocker l(__instanceLock__); diff --git a/AsyncDisplayKit/ASTableNode.mm b/AsyncDisplayKit/ASTableNode.mm index 024f207ae8..7ea4cc2cf5 100644 --- a/AsyncDisplayKit/ASTableNode.mm +++ b/AsyncDisplayKit/ASTableNode.mm @@ -136,10 +136,10 @@ [self.rangeController clearContents]; } -- (void)clearFetchedData +- (void)didExitPreloadState { - [super clearFetchedData]; - [self.rangeController clearFetchedData]; + [super didExitPreloadState]; + [self.rangeController clearPreloadedData]; } - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 0072a57061..ace8e81e5b 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -1646,18 +1646,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [super endUpdates]; } -#pragma mark - Memory Management - -- (void)clearContents -{ - [_rangeController clearContents]; -} - -- (void)clearFetchedData -{ - [_rangeController clearFetchedData]; -} - #pragma mark - Helper Methods // Note: This is called every layout, and so it is very performance sensitive. diff --git a/AsyncDisplayKit/ASVideoNode.mm b/AsyncDisplayKit/ASVideoNode.mm index 9dc4d22077..878189deb3 100644 --- a/AsyncDisplayKit/ASVideoNode.mm +++ b/AsyncDisplayKit/ASVideoNode.mm @@ -11,6 +11,7 @@ #import #import "ASDisplayNodeInternal.h" #import "ASDisplayNode+Subclasses.h" +#import "ASDisplayNode+FrameworkPrivate.h" #import "ASVideoNode.h" #import "ASEqualityHelpers.h" #import "ASInternalHelpers.h" @@ -365,9 +366,9 @@ static NSString * const kRate = @"rate"; } } -- (void)fetchData +- (void)didEnterPreloadState { - [super fetchData]; + [super didEnterPreloadState]; ASDN::MutexLocker l(__instanceLock__); AVAsset *asset = self.asset; @@ -405,9 +406,9 @@ static NSString * const kRate = @"rate"; } } -- (void)clearFetchedData +- (void)didExitPreloadState { - [super clearFetchedData]; + [super didExitPreloadState]; { ASDN::MutexLocker l(__instanceLock__); @@ -505,10 +506,10 @@ static NSString * const kRate = @"rate"; - (void)_setAndFetchAsset:(AVAsset *)asset url:(NSURL *)assetURL { - [self clearFetchedData]; + [self didExitPreloadState]; _asset = asset; _assetURL = assetURL; - [self setNeedsDataFetch]; + [self setNeedsPreload]; } - (void)setVideoComposition:(AVVideoComposition *)videoComposition @@ -617,7 +618,7 @@ static NSString * const kRate = @"rate"; } if (_player == nil) { - [self setNeedsDataFetch]; + [self setNeedsPreload]; } if (_playerNode == nil) { diff --git a/AsyncDisplayKit/Details/ASImageProtocols.h b/AsyncDisplayKit/Details/ASImageProtocols.h index 721f8faeea..159a1084f9 100644 --- a/AsyncDisplayKit/Details/ASImageProtocols.h +++ b/AsyncDisplayKit/Details/ASImageProtocols.h @@ -55,7 +55,7 @@ typedef void(^ASImageCacherCompletion)(id _Nullable i completion:(ASImageCacherCompletion)completion; /** - @abstract Called during clearFetchedData. Allows the cache to optionally trim items. + @abstract Called during clearPreloadedData. Allows the cache to optionally trim items. @note Depending on your caches implementation you may *not* wish to respond to this method. It is however useful if you have a memory and disk cache in which case you'll likely want to clear out the memory cache. */ diff --git a/AsyncDisplayKit/Details/ASLayoutRangeType.h b/AsyncDisplayKit/Details/ASLayoutRangeType.h index d978da4580..068808ecdc 100644 --- a/AsyncDisplayKit/Details/ASLayoutRangeType.h +++ b/AsyncDisplayKit/Details/ASLayoutRangeType.h @@ -31,8 +31,8 @@ typedef NS_ENUM(NSUInteger, ASLayoutRangeMode) { ASLayoutRangeModeFull, /** - * Visible Only mode is used when a range controller should set its display and fetch data regions to only the size of their bounds. - * This causes all additional backing stores & fetched data to be released, while ensuring a user revisiting the view will + * Visible Only mode is used when a range controller should set its display and preload regions to only the size of their bounds. + * This causes all additional backing stores & preloaded data to be released, while ensuring a user revisiting the view will * still be able to see the expected content. This mode is automatically set on all ASRangeControllers when the app suspends, * allowing the operating system to keep the app alive longer and increase the chance it is still warm when the user returns. */ @@ -40,7 +40,7 @@ typedef NS_ENUM(NSUInteger, ASLayoutRangeMode) { /** * Low Memory mode is used when a range controller should discard ALL graphics buffers, including for the area that would be visible - * the next time the user views it (bounds). The only range it preserves is Fetch Data, which is limited to the bounds, allowing + * the next time the user views it (bounds). The only range it preserves is Preload, which is limited to the bounds, allowing * the content to be restored relatively quickly by re-decoding images (the compressed images are ~10% the size of the decoded ones, * and text is a tiny fraction of its rendered size). */ diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index 3b6e2ee46a..9c1a6cd4d4 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -69,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN // These methods call the corresponding method on each node, visiting each one that // the range controller has set a non-default interface state on. - (void)clearContents; -- (void)clearFetchedData; +- (void)clearPreloadedData; /** * An object that describes the layout behavior of the ranged component (table view, collection view, etc.) diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 4702bedb81..7c7febdfa0 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -250,7 +250,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; // Typically the preloadIndexPaths will be the largest, and be a superset of the others, though it may be disjoint. // Because allIndexPaths is an NSMutableOrderedSet, this adds the non-duplicate items /after/ the existing items. - // This means that during iteration, we will first visit visible, then display, then fetch data nodes. + // This means that during iteration, we will first visit visible, then display, then preload nodes. [allIndexPaths unionSet:displayIndexPaths]; [allIndexPaths unionSet:preloadIndexPaths]; @@ -292,14 +292,14 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; } } else { // If selfInterfaceState isn't visible, then visibleIndexPaths represents what /will/ be immediately visible at the - // instant we come onscreen. So, fetch data and display all of those things, but don't waste resources preloading yet. + // instant we come onscreen. So, preload and display all of those things, but don't waste resources preloading yet. // We handle this as a separate case to minimize set operations for offscreen preloading, including containsObject:. if ([allCurrentIndexPaths containsObject:indexPath]) { // DO NOT set Visible: even though these elements are in the visible range / "viewport", // our overall container object is itself not visible yet. The moment it becomes visible, we will run the condition above - // Set Layout, Fetch Data + // Set Layout, Preload interfaceState |= ASInterfaceStatePreload; if (rangeMode != ASLayoutRangeModeLowMemory) { @@ -501,7 +501,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; } } -- (void)clearFetchedData +- (void)clearPreloadedData { for (NSArray *section in [_dataSource completedNodes]) { for (ASDisplayNode *node in section) { diff --git a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h index bbad40d6f0..a6028f156e 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h +++ b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h @@ -156,6 +156,31 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat */ - (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously; +/** + * @abstract Calls -didExitPreloadState on the receiver and its subnode hierarchy. + * + * @discussion Clears any memory-intensive preloaded content. + * This method is used to notify the node that it should purge any content that is both expensive to fetch and to + * retain in memory. + * + * @see [ASDisplayNode(Subclassing) didExitPreloadState] and [ASDisplayNode(Subclassing) didEnterPreloadState] + */ +- (void)recursivelyClearPreloadedData; + +/** + * @abstract Calls -didEnterPreloadState on the receiver and its subnode hierarchy. + * + * @discussion Fetches content from remote sources for the current node and all subnodes. + * + * @see [ASDisplayNode(Subclassing) didEnterPreloadState] and [ASDisplayNode(Subclassing) didExitPreloadState] + */ +- (void)recursivelyPreload; + +/** + * @abstract Triggers a recursive call to -didEnterPreloadState when the node has an interfaceState of ASInterfaceStatePreload + */ +- (void)setNeedsPreload; + /** * @abstract Allows a node to bypass all ensureDisplay passes. Defaults to NO. * diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 16720a6020..e10392dd8d 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -44,7 +44,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1, ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2, ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3, - ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4 + ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4, + ASDisplayNodeMethodOverrideFetchData = 1 << 5, + ASDisplayNodeMethodOverrideClearFetchedData = 1 << 6 }; FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification; diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 6f9c5c08af..6194ca57e3 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -89,7 +89,6 @@ for (ASDisplayNode *n in @[ nodes ]) {\ @interface ASTestDisplayNode : ASDisplayNode @property (nonatomic, copy) void (^willDeallocBlock)(__unsafe_unretained ASTestDisplayNode *node); @property (nonatomic, copy) CGSize(^calculateSizeBlock)(ASTestDisplayNode *node, CGSize size); -@property (nonatomic) BOOL hasFetchedData; @property (nonatomic, nullable) UIGestureRecognizer *gestureRecognizer; @property (nonatomic, nullable) id idGestureRecognizer; @@ -99,6 +98,7 @@ for (ASDisplayNode *n in @[ nodes ]) {\ @property (nonatomic) BOOL displayRangeStateChangedToYES; @property (nonatomic) BOOL displayRangeStateChangedToNO; +@property (nonatomic) BOOL hasPreloaded; @property (nonatomic) BOOL preloadStateChangedToYES; @property (nonatomic) BOOL preloadStateChangedToNO; @end @@ -113,18 +113,6 @@ for (ASDisplayNode *n in @[ nodes ]) {\ return _calculateSizeBlock ? _calculateSizeBlock(self, constrainedSize) : CGSizeZero; } -- (void)fetchData -{ - [super fetchData]; - self.hasFetchedData = YES; -} - -- (void)clearFetchedData -{ - [super clearFetchedData]; - self.hasFetchedData = NO; -} - - (void)didEnterDisplayState { [super didEnterDisplayState]; @@ -141,6 +129,7 @@ for (ASDisplayNode *n in @[ nodes ]) {\ { [super didEnterPreloadState]; self.preloadStateChangedToYES = YES; + self.hasPreloaded = YES; } - (void)didExitPreloadState @@ -1738,76 +1727,76 @@ static inline BOOL _CGPointEqualToPointWithEpsilon(CGPoint point1, CGPoint point } // Check that nodes who have no cell node (no range controller) -// do get their `fetchData` called, and they do report -// the fetch data interface state. +// do get their `preload` called, and they do report +// the preload interface state. - (void)testInterfaceStateForNonCellNode { ASTestWindow *window = [ASTestWindow new]; ASTestDisplayNode *node = [ASTestDisplayNode new]; XCTAssert(node.interfaceState == ASInterfaceStateNone); - XCTAssert(!node.hasFetchedData); + XCTAssert(!node.hasPreloaded); [window addSubview:node.view]; - XCTAssert(node.hasFetchedData); + XCTAssert(node.hasPreloaded); XCTAssert(node.interfaceState == ASInterfaceStateInHierarchy); [node.view removeFromSuperview]; - // We don't want to call -clearFetchedData on nodes that aren't being managed by a range controller. + // We don't want to call -didExitPreloadState on nodes that aren't being managed by a range controller. // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop. // Still, the interfaceState should be None to reflect the current state of the node. // We just don't proactively clear contents or fetched data for this state transition. - XCTAssert(node.hasFetchedData); + XCTAssert(node.hasPreloaded); XCTAssert(node.interfaceState == ASInterfaceStateNone); } // Check that nodes who have no cell node (no range controller) -// do get their `fetchData` called, and they do report -// the fetch data interface state. +// do get their `preload` called, and they do report +// the preload interface state. - (void)testInterfaceStateForCellNode { ASCellNode *cellNode = [ASCellNode new]; ASTestDisplayNode *node = [ASTestDisplayNode new]; XCTAssert(node.interfaceState == ASInterfaceStateNone); - XCTAssert(!node.hasFetchedData); + XCTAssert(!node.hasPreloaded); // Simulate range handler updating cell node. [cellNode addSubnode:node]; [cellNode enterInterfaceState:ASInterfaceStatePreload]; - XCTAssert(node.hasFetchedData); + XCTAssert(node.hasPreloaded); XCTAssert(node.interfaceState == ASInterfaceStatePreload); // If the node goes into a view it should not adopt the `InHierarchy` state. ASTestWindow *window = [ASTestWindow new]; [window addSubview:cellNode.view]; - XCTAssert(node.hasFetchedData); + XCTAssert(node.hasPreloaded); XCTAssert(node.interfaceState == ASInterfaceStateInHierarchy); } -- (void)testSetNeedsDataFetchImmediateState +- (void)testSetNeedsPreloadImmediateState { ASCellNode *cellNode = [ASCellNode new]; ASTestDisplayNode *node = [ASTestDisplayNode new]; [cellNode addSubnode:node]; [cellNode enterInterfaceState:ASInterfaceStatePreload]; - node.hasFetchedData = NO; - [cellNode setNeedsDataFetch]; - XCTAssert(node.hasFetchedData); + node.hasPreloaded = NO; + [cellNode setNeedsPreload]; + XCTAssert(node.hasPreloaded); } -- (void)testFetchDataExitingAndEnteringRange +- (void)testPreloadExitingAndEnteringRange { ASCellNode *cellNode = [ASCellNode new]; ASTestDisplayNode *node = [ASTestDisplayNode new]; [cellNode addSubnode:node]; [cellNode setHierarchyState:ASHierarchyStateRangeManaged]; - // Simulate enter range, fetch data, exit range + // Simulate enter range, preload, exit range [cellNode enterInterfaceState:ASInterfaceStatePreload]; [cellNode exitInterfaceState:ASInterfaceStatePreload]; - node.hasFetchedData = NO; + node.hasPreloaded = NO; [cellNode enterInterfaceState:ASInterfaceStatePreload]; - XCTAssert(node.hasFetchedData); + XCTAssert(node.hasPreloaded); } - (void)testInitWithViewClass @@ -2070,8 +2059,8 @@ static bool stringContainsPointer(NSString *description, id p) { XCTAssertTrue((node.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload); XCTAssertTrue((subnode.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload); - XCTAssertTrue(node.hasFetchedData); - XCTAssertTrue(subnode.hasFetchedData); + XCTAssertTrue(node.hasPreloaded); + XCTAssertTrue(subnode.hasPreloaded); } // FIXME diff --git a/AsyncDisplayKitTests/ASVideoNodeTests.m b/AsyncDisplayKitTests/ASVideoNodeTests.m index c7a0919e66..edbc57ece1 100644 --- a/AsyncDisplayKitTests/ASVideoNodeTests.m +++ b/AsyncDisplayKitTests/ASVideoNodeTests.m @@ -133,7 +133,7 @@ XCTAssertNil(_videoNode.player); } -- (void)testPlayerIsCreatedAsynchronouslyInFetchData +- (void)testPlayerIsCreatedAsynchronouslyInPreload { AVAsset *asset = _firstAsset; @@ -151,7 +151,7 @@ XCTAssertNotNil(_videoNode.player); } -- (void)testPlayerIsCreatedAsynchronouslyInFetchDataWithURL +- (void)testPlayerIsCreatedAsynchronouslyInPreloadWithURL { AVAsset *asset = [AVAsset assetWithURL:_url]; @@ -387,7 +387,7 @@ XCTAssertNotEqual(firstImage, _videoNode.image); } -- (void)testClearingFetchedContentShouldClearAssetData +- (void)testClearingPreloadedContentShouldClearAssetData { AVAsset *asset = _firstAsset; @@ -398,7 +398,7 @@ [[[videoNodeMock expect] andForwardToRealObject] prepareToPlayAsset:assetMock withKeys:_requestedKeys]; _videoNode.asset = assetMock; - [_videoNode fetchData]; + [_videoNode didEnterPreloadState]; [_videoNode setVideoPlaceholderImage:[[UIImage alloc] init]]; [videoNodeMock verifyWithDelay:1.0f]; @@ -407,7 +407,7 @@ XCTAssertNotNil(_videoNode.currentItem); XCTAssertNotNil(_videoNode.image); - [_videoNode clearFetchedData]; + [_videoNode didExitPreloadState]; XCTAssertNil(_videoNode.player); XCTAssertNil(_videoNode.currentItem); XCTAssertNil(_videoNode.image);