Introduced ASHierarchyState. Created ASDisplayNode+FrameworkPrivate.h. Fixed deadlock.

This commit is contained in:
Scott Goodson
2015-12-05 22:20:16 -08:00
parent 29897297c5
commit 840884272d
21 changed files with 281 additions and 153 deletions

View File

@@ -441,6 +441,8 @@
D785F6621A74327E00291744 /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D785F6601A74327E00291744 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; D785F6621A74327E00291744 /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D785F6601A74327E00291744 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; }; D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; };
DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; }; DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; };
DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
DECBD6E71BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; DECBD6E71BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; }; DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
@@ -725,6 +727,7 @@
D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = "<group>"; }; D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = "<group>"; };
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; }; D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = "<group>"; }; D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = "<group>"; };
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkPrivate.h"; sourceTree = "<group>"; };
DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = "<group>"; }; DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = "<group>"; };
DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = "<group>"; }; DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = "<group>"; };
EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1045,6 +1048,7 @@
058D0A09195D050800B7D73C /* ASDisplayNode+DebugTiming.h */, 058D0A09195D050800B7D73C /* ASDisplayNode+DebugTiming.h */,
058D0A0A195D050800B7D73C /* ASDisplayNode+DebugTiming.mm */, 058D0A0A195D050800B7D73C /* ASDisplayNode+DebugTiming.mm */,
058D0A0B195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm */, 058D0A0B195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm */,
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */,
058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */, 058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */,
058D0A0D195D050800B7D73C /* ASImageNode+CGExtras.h */, 058D0A0D195D050800B7D73C /* ASImageNode+CGExtras.h */,
058D0A0E195D050800B7D73C /* ASImageNode+CGExtras.m */, 058D0A0E195D050800B7D73C /* ASImageNode+CGExtras.m */,
@@ -1231,6 +1235,7 @@
257754B11BEE44CD00737CA5 /* ASTextKitShadower.h in Headers */, 257754B11BEE44CD00737CA5 /* ASTextKitShadower.h in Headers */,
058D0A7B195D05F900B7D73C /* ASDisplayNodeInternal.h in Headers */, 058D0A7B195D05F900B7D73C /* ASDisplayNodeInternal.h in Headers */,
0587F9BD1A7309ED00AFF0BA /* ASEditableTextNode.h in Headers */, 0587F9BD1A7309ED00AFF0BA /* ASEditableTextNode.h in Headers */,
DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */,
1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */, 1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */,
257754A81BEE44CD00737CA5 /* ASTextKitContext.h in Headers */, 257754A81BEE44CD00737CA5 /* ASTextKitContext.h in Headers */,
464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */, 464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */,
@@ -1340,6 +1345,7 @@
254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */, 254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */,
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */, 509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */,
B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */, B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */,
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */,
B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */,
B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */,
B35062171B010EFD0018CF92 /* ASDataController.h in Headers */, B35062171B010EFD0018CF92 /* ASDataController.h in Headers */,

View File

@@ -25,7 +25,7 @@
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
// use UITableViewCell defaults // Use UITableViewCell defaults
_selectionStyle = UITableViewCellSelectionStyleDefault; _selectionStyle = UITableViewCellSelectionStyleDefault;
self.clipsToBounds = YES; self.clipsToBounds = YES;

View File

@@ -6,19 +6,16 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*/ */
#import "ASCollectionView.h"
#import "ASAssert.h" #import "ASAssert.h"
#import "ASCollectionViewLayoutController.h"
#import "ASRangeController.h"
#import "ASCollectionDataController.h"
#import "ASBatchFetching.h" #import "ASBatchFetching.h"
#import "UICollectionViewLayout+ASConvenience.h" #import "ASCollectionView.h"
#import "ASInternalHelpers.h" #import "ASCollectionDataController.h"
#import "ASCollectionViewLayoutController.h"
#import "ASCollectionViewFlowLayoutInspector.h" #import "ASCollectionViewFlowLayoutInspector.h"
#import "ASDisplayNode+FrameworkPrivate.h"
// FIXME: Temporary nonsense import until method names are finalized and exposed #import "ASInternalHelpers.h"
#import "ASDisplayNode+Subclasses.h" #import "ASRangeController.h"
#import "UICollectionViewLayout+ASConvenience.h"
static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone; static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone;
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero}; static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
@@ -661,6 +658,8 @@ static BOOL _isInterceptedSelector(SEL sel)
- (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath - (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath
{ {
ASCellNode *node = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath]; ASCellNode *node = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath];
[node enterHierarchyState:ASHierarchyStateRangeManaged];
ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode"); ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode");
if (node.layoutDelegate == nil) { if (node.layoutDelegate == nil) {
node.layoutDelegate = self; node.layoutDelegate = self;

View File

@@ -412,10 +412,8 @@
*/ */
- (UIImage *)placeholderImage; - (UIImage *)placeholderImage;
/** @name Description */ /** @name Description */
/** /**
* @abstract Return a description of the node * @abstract Return a description of the node
* *
@@ -425,47 +423,5 @@
@end @end
@interface ASDisplayNode (ASDisplayNodePrivate)
/**
* This method has proven helpful in a few rare scenarios, similar to a category extension on UIView,
* but it's considered private API for now and its use should not be encouraged.
* @param checkViewHierarchy If YES, and no supernode can be found, method will walk up from `self.view` to find a supernode.
* If YES, this method must be called on the main thread and the node must not be layer-backed.
*/
- (ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy;
// The two methods below will eventually be exposed, but their names are subject to change.
/**
* @abstract Ensure that all rendering is complete for this node and its descendents.
*
* @discussion Calling this method on the main thread after a node is added to the view heirarchy will ensure that
* placeholder states are never visible to the user. It is used by ASTableView, ASCollectionView, and ASViewController
* to implement their respective ".neverShowPlaceholders" option.
*
* If all nodes have layer.contents set and/or their layer does not have -needsDisplay set, the method will return immediately.
*
* This method is capable of handling a mixed set of nodes, with some not having started display, some in progress on an
* asynchronous display operation, and some already finished.
*
* In order to guarantee against deadlocks, this method should only be called on the main thread.
* It may block on the private queue, [_ASDisplayLayer displayQueue]
*/
- (void)recursivelyEnsureDisplay;
/**
* @abstract Allows a node to bypass all ensureDisplay passes. Defaults to NO.
*
* @discussion Nodes that are expensive to draw and expected to have placeholder even with
* .neverShowPlaceholders enabled should set this to YES.
*
* ASImageNode uses the default of NO, as it is often used for UI images that are expected to synchronize with ensureDisplay.
*
* ASNetworkImageNode and ASMultiplexImageNode set this to YES, because they load data from a database or server,
* and are expected to support a placeholder state given that display is often blocked on slow data fetching.
*/
@property (nonatomic, assign) BOOL shouldBypassEnsureDisplay;
@end
#define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created") #define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
#define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created") #define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")

View File

@@ -7,8 +7,9 @@
*/ */
#import "ASDisplayNode.h" #import "ASDisplayNode.h"
#import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import "ASLayoutOptionsPrivate.h" #import "ASLayoutOptionsPrivate.h"
#import <objc/runtime.h> #import <objc/runtime.h>
@@ -38,6 +39,9 @@
@end @end
//#define LOG(...) NSLog(__VA_ARGS__)
#define LOG(...)
// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds) // Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
#if TIME_DISPLAYNODE_OPS #if TIME_DISPLAYNODE_OPS
#define TIME_SCOPED(outVar) ASDN::ScopeTimer t(outVar) #define TIME_SCOPED(outVar) ASDN::ScopeTimer t(outVar)
@@ -323,17 +327,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
#pragma mark - Core #pragma mark - Core
- (void)__tearDown:(BOOL)tearDown subnodesOfNode:(ASDisplayNode *)node
{
for (ASDisplayNode *subnode in node.subnodes) {
if (tearDown) {
[subnode __unloadNode];
} else {
[subnode __loadNode];
}
}
}
- (void)__unloadNode - (void)__unloadNode
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
@@ -357,22 +350,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self layer]; [self layer];
} }
- (ASDisplayNode *)__rasterizedContainerNode
{
ASDisplayNode *node = self.supernode;
while (node) {
if (node.shouldRasterizeDescendants) {
return node;
}
node = node.supernode;
}
return nil;
}
- (BOOL)__shouldLoadViewOrLayer - (BOOL)__shouldLoadViewOrLayer
{ {
return ![self __rasterizedContainerNode]; return !(_hierarchyState & ASHierarchyStateRasterized);
} }
- (BOOL)__shouldSize - (BOOL)__shouldSize
@@ -645,30 +625,44 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
ASDisplayNodeAssert(!((_hierarchyState & ASHierarchyStateRasterized) && _flags.shouldRasterizeDescendants),
@"Subnode of a rasterized node should not have redundant shouldRasterizeDescendants enabled");
return _flags.shouldRasterizeDescendants; return _flags.shouldRasterizeDescendants;
} }
- (void)setShouldRasterizeDescendants:(BOOL)flag - (void)setShouldRasterizeDescendants:(BOOL)shouldRasterize
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
if (_flags.shouldRasterizeDescendants == flag) if (_flags.shouldRasterizeDescendants == shouldRasterize)
return; return;
_flags.shouldRasterizeDescendants = flag; _flags.shouldRasterizeDescendants = shouldRasterize;
if (self.isNodeLoaded) { if (self.isNodeLoaded) {
//recursively tear down or build up subnodes // Recursively tear down or build up subnodes.
// TODO: When disabling rasterization, preserve rasterized backing store as placeholderImage
// while the newly materialized subtree finishes rendering. Then destroy placeholderImage to save memory.
[self recursivelyClearContents]; [self recursivelyClearContents];
[self __tearDown:flag subnodesOfNode:self];
if (flag == NO) {
[self _addSubnodeViewsAndLayers];
}
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode *node) {
if (shouldRasterize) {
[node enterHierarchyState:ASHierarchyStateRasterized];
[node __unloadNode];
} else {
[node exitHierarchyState:ASHierarchyStateRasterized];
[node __loadNode];
}
});
if (self.interfaceState & ASInterfaceStateVisible) {
// TODO: Change this to recursivelyEnsureDisplay - but need a variant that does not skip
// nodes that have shouldBypassEnsureDisplay set (such as image nodes) so they are rasterized.
[self recursivelyDisplayImmediately]; [self recursivelyDisplayImmediately];
} }
} }
}
- (CGFloat)contentsScaleForDisplay - (CGFloat)contentsScaleForDisplay
{ {
@@ -969,7 +963,7 @@ static bool disableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASD
[_subnodes insertObject:subnode atIndex:subnodeIndex]; [_subnodes insertObject:subnode atIndex:subnodeIndex];
// Don't bother inserting the view/layer if in a rasterized subtree, becuase there are no layers in the hierarchy and none of this could possibly work. // Don't bother inserting the view/layer if in a rasterized subtree, becuase there are no layers in the hierarchy and none of this could possibly work.
if (!_flags.shouldRasterizeDescendants && ![self __rasterizedContainerNode]) { if (!_flags.shouldRasterizeDescendants && [self __shouldLoadViewOrLayer]) {
if (_layer) { if (_layer) {
ASDisplayNodeCAssertMainThread(); ASDisplayNodeCAssertMainThread();
@@ -1090,7 +1084,7 @@ static NSInteger incrementIfFound(NSInteger i) {
NSInteger aboveSublayerIndex = NSNotFound; NSInteger aboveSublayerIndex = NSNotFound;
// Don't bother figuring out the sublayerIndex if in a rasterized subtree, becuase there are no layers in the hierarchy and none of this could possibly work. // Don't bother figuring out the sublayerIndex if in a rasterized subtree, becuase there are no layers in the hierarchy and none of this could possibly work.
if (!_flags.shouldRasterizeDescendants && ![self __rasterizedContainerNode]) { if (!_flags.shouldRasterizeDescendants && [self __shouldLoadViewOrLayer]) {
if (_layer) { if (_layer) {
aboveSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:above.layer]; aboveSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:above.layer];
ASDisplayNodeAssert(aboveSublayerIndex != NSNotFound, @"Somehow above's supernode is self, yet we could not find it in our layers to replace"); ASDisplayNodeAssert(aboveSublayerIndex != NSNotFound, @"Somehow above's supernode is self, yet we could not find it in our layers to replace");
@@ -1345,7 +1339,15 @@ static NSInteger incrementIfFound(NSInteger i) {
- (void)__setSupernode:(ASDisplayNode *)supernode - (void)__setSupernode:(ASDisplayNode *)supernode
{ {
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
if (_supernode != supernode) {
ASHierarchyState oldHierarchyState = _supernode.hierarchyState;
_supernode = supernode; _supernode = supernode;
if (_supernode) {
[self enterHierarchyState:_supernode.hierarchyState];
} else {
[self exitHierarchyState:oldHierarchyState];
}
}
} }
// Track that a node will be displayed as part of the current node hierarchy. // Track that a node will be displayed as part of the current node hierarchy.
@@ -1653,7 +1655,8 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
* @see https://github.com/facebook/AsyncDisplayKit/issues/900 * @see https://github.com/facebook/AsyncDisplayKit/issues/900
* Possible solution is to push `isInCellNode` state downward on `addSubnode`/`removeFromSupernode`. * Possible solution is to push `isInCellNode` state downward on `addSubnode`/`removeFromSupernode`.
*/ */
- (BOOL)supportsInterfaceState { - (BOOL)supportsInterfaceState
{
return ([self isKindOfClass:ASCellNode.class] return ([self isKindOfClass:ASCellNode.class]
|| [self _supernodeWithClass:ASCellNode.class checkViewHierarchy:NO] != nil); || [self _supernodeWithClass:ASCellNode.class checkViewHierarchy:NO] != nil);
} }
@@ -1664,23 +1667,23 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
return _interfaceState; return _interfaceState;
} }
- (void)setInterfaceState:(ASInterfaceState)interfaceState - (void)setInterfaceState:(ASInterfaceState)newState
{ {
ASInterfaceState oldValue; ASInterfaceState oldState;
{ {
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
oldValue = _interfaceState; oldState = _interfaceState;
_interfaceState = interfaceState; _interfaceState = newState;
} }
if (interfaceState != oldValue) { if (newState != oldState) {
if ((interfaceState & ASInterfaceStateMeasureLayout) != (oldValue & ASInterfaceStateMeasureLayout)) { if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) {
// Trigger asynchronous measurement if it is not already cached or being calculated. // Trigger asynchronous measurement if it is not already cached or being calculated.
} }
// Entered or exited data loading state. // Entered or exited data loading state.
if ((interfaceState & ASInterfaceStateFetchData) != (oldValue & ASInterfaceStateFetchData)) { if ((newState & ASInterfaceStateFetchData) != (oldState & ASInterfaceStateFetchData)) {
if (interfaceState & ASInterfaceStateFetchData) { if (newState & ASInterfaceStateFetchData) {
[self fetchData]; [self fetchData];
} else { } else {
[self clearFetchedData]; [self clearFetchedData];
@@ -1688,8 +1691,8 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
} }
// Entered or exited contents rendering state. // Entered or exited contents rendering state.
if ((interfaceState & ASInterfaceStateDisplay) != (oldValue & ASInterfaceStateDisplay)) { if ((newState & ASInterfaceStateDisplay) != (oldState & ASInterfaceStateDisplay)) {
if (interfaceState & ASInterfaceStateDisplay) { if (newState & ASInterfaceStateDisplay) {
// Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here. // Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here.
[self setDisplaySuspended:NO]; [self setDisplaySuspended:NO];
} else { } else {
@@ -1699,14 +1702,13 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
} }
// Entered or exited data loading state. // Entered or exited data loading state.
if ((interfaceState & ASInterfaceStateVisible) != (oldValue & ASInterfaceStateVisible)) { if ((newState & ASInterfaceStateVisible) != (oldState & ASInterfaceStateVisible)) {
if (interfaceState & ASInterfaceStateVisible) { if (newState & ASInterfaceStateVisible) {
// Consider providing a -didBecomeVisible. // Consider providing a -didBecomeVisible.
} else { } else {
// Consider providing a -didBecomeInvisible. // Consider providing a -didBecomeInvisible.
} }
} }
} }
} }
@@ -1724,6 +1726,40 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
}); });
} }
- (ASHierarchyState)hierarchyState
{
ASDN::MutexLocker l(_propertyLock);
return _hierarchyState;
}
- (void)setHierarchyState:(ASHierarchyState)newState
{
ASHierarchyState oldState;
{
ASDN::MutexLocker l(_propertyLock);
oldState = _hierarchyState;
_hierarchyState = newState;
}
if (newState != oldState) {
LOG(@"setHierarchyState: oldState = %lu, newState = %lu", (unsigned long)oldState, (unsigned long)newState);
}
}
- (void)enterHierarchyState:(ASHierarchyState)hierarchyState
{
ASDisplayNodePerformBlockOnEveryNode(nil, self, ^(ASDisplayNode *node) {
node.hierarchyState |= hierarchyState;
});
}
- (void)exitHierarchyState:(ASHierarchyState)hierarchyState
{
ASDisplayNodePerformBlockOnEveryNode(nil, self, ^(ASDisplayNode *node) {
node.hierarchyState &= (~hierarchyState);
});
}
- (void)layout - (void)layout
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();

View File

@@ -37,6 +37,12 @@ extern ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node);
*/ */
extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *node, void(^block)(ASDisplayNode *node)); extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *node, void(^block)(ASDisplayNode *node));
/**
Identical to ASDisplayNodePerformBlockOnEveryNode, except it does not run the block on the
node provided directly to the function call - only on all descendants.
*/
extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, void(^block)(ASDisplayNode *node));
/** /**
Given a display node, traverses up the layer tree hierarchy, returning the first display node that passes block. Given a display node, traverses up the layer tree hierarchy, returning the first display node that passes block.
*/ */

View File

@@ -7,8 +7,8 @@
*/ */
#import "ASDisplayNodeExtras.h" #import "ASDisplayNodeExtras.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+FrameworkPrivate.h"
extern ASDisplayNode *ASLayerToDisplayNode(CALayer *layer) extern ASDisplayNode *ASLayerToDisplayNode(CALayer *layer)
{ {
@@ -46,6 +46,13 @@ extern void ASDisplayNodePerformBlockOnEveryNode(CALayer *layer, ASDisplayNode *
} }
} }
extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, void(^block)(ASDisplayNode *node))
{
for (ASDisplayNode *subnode in node.subnodes) {
ASDisplayNodePerformBlockOnEveryNode(nil, subnode, block);
}
}
id ASDisplayNodeFind(ASDisplayNode *node, BOOL (^block)(ASDisplayNode *node)) id ASDisplayNodeFind(ASDisplayNode *node, BOOL (^block)(ASDisplayNode *node))
{ {
CALayer *layer = node.layer; CALayer *layer = node.layer;

View File

@@ -17,6 +17,7 @@
#import "ASAvailability.h" #import "ASAvailability.h"
#import "ASBaseDefines.h" #import "ASBaseDefines.h"
#import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import "ASLog.h" #import "ASLog.h"
#import "ASPhotosFrameworkImageRequest.h" #import "ASPhotosFrameworkImageRequest.h"
#import "ASEqualityHelpers.h" #import "ASEqualityHelpers.h"

View File

@@ -10,8 +10,9 @@
#import "ASBasicImageDownloader.h" #import "ASBasicImageDownloader.h"
#import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+Subclasses.h"
#import "ASThread.h" #import "ASDisplayNode+FrameworkPrivate.h"
#import "ASEqualityHelpers.h" #import "ASEqualityHelpers.h"
#import "ASThread.h"
@interface ASNetworkImageNode () @interface ASNetworkImageNode ()
{ {
@@ -30,10 +31,8 @@
BOOL _imageLoaded; BOOL _imageLoaded;
} }
@end @end
@implementation ASNetworkImageNode @implementation ASNetworkImageNode
- (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASImageDownloaderProtocol>)downloader - (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASImageDownloaderProtocol>)downloader

View File

@@ -10,16 +10,14 @@
#import "ASTableViewInternal.h" #import "ASTableViewInternal.h"
#import "ASAssert.h" #import "ASAssert.h"
#import "ASBatchFetching.h"
#import "ASChangeSetDataController.h" #import "ASChangeSetDataController.h"
#import "ASCollectionViewLayoutController.h" #import "ASCollectionViewLayoutController.h"
#import "ASLayoutController.h" #import "ASDisplayNode+FrameworkPrivate.h"
#import "ASRangeController.h"
#import "ASBatchFetching.h"
#import "ASInternalHelpers.h" #import "ASInternalHelpers.h"
#import "ASLayout.h" #import "ASLayout.h"
#import "ASLayoutController.h"
// FIXME: Temporary nonsense import until method names are finalized and exposed #import "ASRangeController.h"
#import "ASDisplayNode+Subclasses.h"
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
@@ -829,6 +827,8 @@ static BOOL _isInterceptedSelector(SEL sel)
- (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath - (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath
{ {
ASCellNode *node = [_asyncDataSource tableView:self nodeForRowAtIndexPath:indexPath]; ASCellNode *node = [_asyncDataSource tableView:self nodeForRowAtIndexPath:indexPath];
[node enterHierarchyState:ASHierarchyStateRangeManaged];
ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode"); ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode");
if (node.layoutDelegate == nil) { if (node.layoutDelegate == nil) {
node.layoutDelegate = self; node.layoutDelegate = self;

View File

@@ -9,9 +9,7 @@
#import "ASViewController.h" #import "ASViewController.h"
#import "ASAssert.h" #import "ASAssert.h"
#import "ASDimension.h" #import "ASDimension.h"
#import "ASDisplayNode+FrameworkPrivate.h"
// FIXME: Temporary nonsense import until method names are finalized and exposed
#import "ASDisplayNode+Subclasses.h"
@implementation ASViewController @implementation ASViewController
{ {

View File

@@ -8,7 +8,7 @@
#import "ASRangeHandlerPreload.h" #import "ASRangeHandlerPreload.h"
#import "ASDisplayNode.h" #import "ASDisplayNode.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNode+FrameworkPrivate.h"
@implementation ASRangeHandlerPreload @implementation ASRangeHandlerPreload

View File

@@ -10,7 +10,7 @@
#import "ASDisplayNode.h" #import "ASDisplayNode.h"
#import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNode+FrameworkPrivate.h"
@interface ASRangeHandlerRender () @interface ASRangeHandlerRender ()
@property (nonatomic,readonly) UIWindow *workingWindow; @property (nonatomic,readonly) UIWindow *workingWindow;

View File

@@ -8,7 +8,7 @@
#import "ASRangeHandlerVisible.h" #import "ASRangeHandlerVisible.h"
#import "ASDisplayNode.h" #import "ASDisplayNode.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNode+FrameworkPrivate.h"
@implementation ASRangeHandlerVisible @implementation ASRangeHandlerVisible

View File

@@ -14,6 +14,7 @@
#import "ASAssert.h" #import "ASAssert.h"
#import "ASDisplayNode.h" #import "ASDisplayNode.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+FrameworkPrivate.h"
@implementation _ASDisplayLayer @implementation _ASDisplayLayer
{ {

View File

@@ -15,6 +15,7 @@
#import "ASAssert.h" #import "ASAssert.h"
#import "ASDisplayNodeExtras.h" #import "ASDisplayNodeExtras.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+Subclasses.h"
@interface _ASDisplayView () @interface _ASDisplayView ()

View File

@@ -10,6 +10,7 @@
#import "_ASAsyncTransaction.h" #import "_ASAsyncTransaction.h"
#import "ASAssert.h" #import "ASAssert.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+FrameworkPrivate.h"
@implementation ASDisplayNode (AsyncDisplay) @implementation ASDisplayNode (AsyncDisplay)
@@ -84,7 +85,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
return; return;
} }
BOOL rasterizingFromAscendent = [self __rasterizedContainerNode] != nil; BOOL rasterizingFromAscendent = (_hierarchyState & ASHierarchyStateRasterized);
// if super node is rasterizing descendents, subnodes will not have had layout calls becase they don't have layers // if super node is rasterizing descendents, subnodes will not have had layout calls becase they don't have layers
if (rasterizingFromAscendent) { if (rasterizingFromAscendent) {
@@ -178,7 +179,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil; asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil;
ASDisplayNodeAssert(rasterizing || ![self __rasterizedContainerNode], @"Rasterized descendants should never display unless being drawn into the rasterized container."); ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized), @"Rasterized descendants should never display unless being drawn into the rasterized container.");
if (!rasterizing && self.shouldRasterizeDescendants) { if (!rasterizing && self.shouldRasterizeDescendants) {
CGRect bounds = self.bounds; CGRect bounds = self.bounds;
@@ -296,7 +297,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
if ([self __rasterizedContainerNode]) { if (_hierarchyState & ASHierarchyStateRasterized) {
return; return;
} }

View File

@@ -0,0 +1,109 @@
/* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
//
// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode.
// These methods must never be called or overridden by other classes.
//
#import "_ASDisplayLayer.h"
#import "_AS-objc-internal.h"
#import "ASDisplayNodeExtraIvars.h"
#import "ASDisplayNode.h"
#import "ASSentinel.h"
#import "ASThread.h"
#import "ASLayoutOptions.h"
/**
Hierarchy state is propogated from nodes to all of their children when certain behaviors are required from the subtree.
Examples include rasterization and external driving of the .interfaceState property.
By passing this information explicitly, performance is optimized by avoiding iteration up the supernode chain.
Lastly, this avoidance of supernode traversal protects against the possibility of deadlocks when a supernode is
simultaneously attempting to materialize views / layers for its subtree (as many related methods require property locking)
Note: as the hierarchy deepens, more state properties may be enabled. However, state properties may never be disabled /
cancelled below the point they are enabled. They continue to the leaves of the hierarchy.
*/
typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
{
/** The node may or may not have a supernode, but no supernode has a special hierarchy-influencing option enabled. */
ASHierarchyStateNormal = 0,
/** The node has a supernode with .shouldRasterizeDescendants = YES.
Note: the root node of the rasterized subtree (the one with the property set on it) will NOT have this state set. */
ASHierarchyStateRasterized = 1 << 0,
/** The node or one of its supernodes is managed by a class like ASRangeController. Most commonly, these nodes are
ASCellNode objects or a subnode of one, and are used in ASTableView or ASCollectionView.
These nodes also recieve regular updates to the .interfaceState property with more detailed status information. */
ASHierarchyStateRangeManaged = 1 << 1,
};
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
{
@protected
ASInterfaceState _interfaceState;
ASHierarchyState _hierarchyState;
}
// These methods are recursive, and either union or remove the provided interfaceState to all sub-elements.
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
- (void)exitInterfaceState:(ASInterfaceState)interfaceState;
// These methods are recursive, and either union or remove the provided hierarchyState to all sub-elements.
- (void)enterHierarchyState:(ASHierarchyState)hierarchyState;
- (void)exitHierarchyState:(ASHierarchyState)hierarchyState;
/**
* @abstract Returns the Hierarchy State of the node.
*
* @return The current ASHierarchyState of the node, indicating whether it is rasterized or managed by a range controller.
*
* @see ASInterfaceState
*/
@property (nonatomic, readwrite) ASHierarchyState hierarchyState;
// The two methods below will eventually be exposed, but their names are subject to change.
/**
* @abstract Ensure that all rendering is complete for this node and its descendents.
*
* @discussion Calling this method on the main thread after a node is added to the view heirarchy will ensure that
* placeholder states are never visible to the user. It is used by ASTableView, ASCollectionView, and ASViewController
* to implement their respective ".neverShowPlaceholders" option.
*
* If all nodes have layer.contents set and/or their layer does not have -needsDisplay set, the method will return immediately.
*
* This method is capable of handling a mixed set of nodes, with some not having started display, some in progress on an
* asynchronous display operation, and some already finished.
*
* In order to guarantee against deadlocks, this method should only be called on the main thread.
* It may block on the private queue, [_ASDisplayLayer displayQueue]
*/
- (void)recursivelyEnsureDisplay;
/**
* @abstract Allows a node to bypass all ensureDisplay passes. Defaults to NO.
*
* @discussion Nodes that are expensive to draw and expected to have placeholder even with
* .neverShowPlaceholders enabled should set this to YES.
*
* ASImageNode uses the default of NO, as it is often used for UI images that are expected to synchronize with ensureDisplay.
*
* ASNetworkImageNode and ASMultiplexImageNode set this to YES, because they load data from a database or server,
* and are expected to support a placeholder state given that display is often blocked on slow data fetching.
*/
@property (nonatomic, assign) BOOL shouldBypassEnsureDisplay;
@end
@interface UIView (ASDisplayNodeInternal)
@property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@end
@interface CALayer (ASDisplayNodeInternal)
@property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@end

View File

@@ -8,9 +8,11 @@
#import "_ASCoreAnimationExtras.h" #import "_ASCoreAnimationExtras.h"
#import "_ASPendingState.h" #import "_ASPendingState.h"
#import "ASInternalHelpers.h"
#import "ASAssert.h" #import "ASAssert.h"
#import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNodeInternal.h" #import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import "ASEqualityHelpers.h" #import "ASEqualityHelpers.h"
/** /**
@@ -219,9 +221,22 @@
- (void)setNeedsDisplay - (void)setNeedsDisplay
{ {
ASDisplayNode *rasterizedContainerNode = [self __rasterizedContainerNode]; if (_hierarchyState & ASHierarchyStateRasterized) {
if (rasterizedContainerNode) { ASPerformBlockOnMainThread(^{
// The below operation must be performed on the main thread to ensure against an extremely rare deadlock, where a parent node
// begins materializing the view / layer heirarchy (locking itself or a descendant) while this node walks up
// the tree and requires locking that node to access .shouldRasterizeDescendants.
// For this reason, this method should be avoided when possible. Use _hierarchyState & ASHierarchyStateRasterized.
ASDisplayNodeAssertMainThread();
ASDisplayNode *rasterizedContainerNode = self.supernode;
while (rasterizedContainerNode) {
if (rasterizedContainerNode.shouldRasterizeDescendants) {
break;
}
rasterizedContainerNode = rasterizedContainerNode.supernode;
}
[rasterizedContainerNode setNeedsDisplay]; [rasterizedContainerNode setNeedsDisplay];
});
} else { } else {
[_layer setNeedsDisplay]; [_layer setNeedsDisplay];
} }

View File

@@ -22,7 +22,8 @@
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)()); void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)());
typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
{
ASDisplayNodeMethodOverrideNone = 0, ASDisplayNodeMethodOverrideNone = 0,
ASDisplayNodeMethodOverrideTouchesBegan = 1 << 0, ASDisplayNodeMethodOverrideTouchesBegan = 1 << 0,
ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1, ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1,
@@ -73,8 +74,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
_ASPendingState *_pendingViewState; _ASPendingState *_pendingViewState;
ASInterfaceState _interfaceState;
struct ASDisplayNodeFlags { struct ASDisplayNodeFlags {
// public properties // public properties
unsigned synchronous:1; unsigned synchronous:1;
@@ -118,9 +117,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
// Bitmask to check which methods an object overrides. // Bitmask to check which methods an object overrides.
@property (nonatomic, assign, readonly) ASDisplayNodeMethodOverrides methodOverrides; @property (nonatomic, assign, readonly) ASDisplayNodeMethodOverrides methodOverrides;
// These methods are recursive, and either union or remove the provided interfaceState to all sub-elements.
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
- (void)exitInterfaceState:(ASInterfaceState)interfaceState;
// Swizzle to extend the builtin functionality with custom logic // Swizzle to extend the builtin functionality with custom logic
- (BOOL)__shouldLoadViewOrLayer; - (BOOL)__shouldLoadViewOrLayer;
@@ -149,9 +145,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated. // Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
- (void)displayImmediately; - (void)displayImmediately;
// Returns the ancestor node that rasterizes descendants, or nil if none.
- (ASDisplayNode *)__rasterizedContainerNode;
// Alternative initialiser for backing with a custom view class. Supports asynchronous display with _ASDisplayView subclasses. // Alternative initialiser for backing with a custom view class. Supports asynchronous display with _ASDisplayView subclasses.
- (id)initWithViewClass:(Class)viewClass; - (id)initWithViewClass:(Class)viewClass;
@@ -160,12 +153,12 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
@property (nonatomic, assign) CGFloat contentsScaleForDisplay; @property (nonatomic, assign) CGFloat contentsScaleForDisplay;
@end /**
* This method has proven helpful in a few rare scenarios, similar to a category extension on UIView,
* but it's considered private API for now and its use should not be encouraged.
* @param checkViewHierarchy If YES, and no supernode can be found, method will walk up from `self.view` to find a supernode.
* If YES, this method must be called on the main thread and the node must not be layer-backed.
*/
- (ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy;
@interface UIView (ASDisplayNodeInternal)
@property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@end
@interface CALayer (ASDisplayNodeInternal)
@property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
@end @end

View File

@@ -68,7 +68,7 @@
{ {
// If it's a space character and we have custom word kerning, use the whitespace action control character. // If it's a space character and we have custom word kerning, use the whitespace action control character.
if ([layoutManager.textStorage.string characterAtIndex:characterIndex] == ' ') if ([layoutManager.textStorage.string characterAtIndex:characterIndex] == ' ')
return NSControlCharacterWhitespaceAction; return NSControlCharacterActionWhitespace;
return defaultAction; return defaultAction;
} }