mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Introduced ASHierarchyState. Created ASDisplayNode+FrameworkPrivate.h. Fixed deadlock.
This commit is contained in:
@@ -441,6 +441,8 @@
|
||||
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 */; };
|
||||
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, ); }; };
|
||||
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 */; };
|
||||
@@ -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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
@@ -1045,6 +1048,7 @@
|
||||
058D0A09195D050800B7D73C /* ASDisplayNode+DebugTiming.h */,
|
||||
058D0A0A195D050800B7D73C /* ASDisplayNode+DebugTiming.mm */,
|
||||
058D0A0B195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm */,
|
||||
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */,
|
||||
058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */,
|
||||
058D0A0D195D050800B7D73C /* ASImageNode+CGExtras.h */,
|
||||
058D0A0E195D050800B7D73C /* ASImageNode+CGExtras.m */,
|
||||
@@ -1231,6 +1235,7 @@
|
||||
257754B11BEE44CD00737CA5 /* ASTextKitShadower.h in Headers */,
|
||||
058D0A7B195D05F900B7D73C /* ASDisplayNodeInternal.h in Headers */,
|
||||
0587F9BD1A7309ED00AFF0BA /* ASEditableTextNode.h in Headers */,
|
||||
DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */,
|
||||
1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */,
|
||||
257754A81BEE44CD00737CA5 /* ASTextKitContext.h in Headers */,
|
||||
464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */,
|
||||
@@ -1340,6 +1345,7 @@
|
||||
254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */,
|
||||
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */,
|
||||
B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */,
|
||||
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */,
|
||||
B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */,
|
||||
B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */,
|
||||
B35062171B010EFD0018CF92 /* ASDataController.h in Headers */,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
// use UITableViewCell defaults
|
||||
// Use UITableViewCell defaults
|
||||
_selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
|
||||
@@ -6,19 +6,16 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "ASCollectionView.h"
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASCollectionViewLayoutController.h"
|
||||
#import "ASRangeController.h"
|
||||
#import "ASCollectionDataController.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "UICollectionViewLayout+ASConvenience.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASCollectionView.h"
|
||||
#import "ASCollectionDataController.h"
|
||||
#import "ASCollectionViewLayoutController.h"
|
||||
#import "ASCollectionViewFlowLayoutInspector.h"
|
||||
|
||||
// FIXME: Temporary nonsense import until method names are finalized and exposed
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASRangeController.h"
|
||||
#import "UICollectionViewLayout+ASConvenience.h"
|
||||
|
||||
static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone;
|
||||
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
|
||||
@@ -661,6 +658,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASCellNode *node = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath];
|
||||
[node enterHierarchyState:ASHierarchyStateRangeManaged];
|
||||
|
||||
ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode");
|
||||
if (node.layoutDelegate == nil) {
|
||||
node.layoutDelegate = self;
|
||||
|
||||
@@ -412,10 +412,8 @@
|
||||
*/
|
||||
- (UIImage *)placeholderImage;
|
||||
|
||||
|
||||
/** @name Description */
|
||||
|
||||
|
||||
/**
|
||||
* @abstract Return a description of the node
|
||||
*
|
||||
@@ -425,47 +423,5 @@
|
||||
|
||||
@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 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")
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
*/
|
||||
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASLayoutOptionsPrivate.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
@@ -38,6 +39,9 @@
|
||||
|
||||
@end
|
||||
|
||||
//#define LOG(...) NSLog(__VA_ARGS__)
|
||||
#define LOG(...)
|
||||
|
||||
// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
|
||||
#if TIME_DISPLAYNODE_OPS
|
||||
#define TIME_SCOPED(outVar) ASDN::ScopeTimer t(outVar)
|
||||
@@ -323,17 +327,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
|
||||
#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
|
||||
{
|
||||
ASDisplayNodeAssertThreadAffinity(self);
|
||||
@@ -357,22 +350,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
[self layer];
|
||||
}
|
||||
|
||||
- (ASDisplayNode *)__rasterizedContainerNode
|
||||
{
|
||||
ASDisplayNode *node = self.supernode;
|
||||
while (node) {
|
||||
if (node.shouldRasterizeDescendants) {
|
||||
return node;
|
||||
}
|
||||
node = node.supernode;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)__shouldLoadViewOrLayer
|
||||
{
|
||||
return ![self __rasterizedContainerNode];
|
||||
return !(_hierarchyState & ASHierarchyStateRasterized);
|
||||
}
|
||||
|
||||
- (BOOL)__shouldSize
|
||||
@@ -645,30 +625,44 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
{
|
||||
ASDisplayNodeAssertThreadAffinity(self);
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
ASDisplayNodeAssert(!((_hierarchyState & ASHierarchyStateRasterized) && _flags.shouldRasterizeDescendants),
|
||||
@"Subnode of a rasterized node should not have redundant shouldRasterizeDescendants enabled");
|
||||
return _flags.shouldRasterizeDescendants;
|
||||
}
|
||||
|
||||
- (void)setShouldRasterizeDescendants:(BOOL)flag
|
||||
- (void)setShouldRasterizeDescendants:(BOOL)shouldRasterize
|
||||
{
|
||||
ASDisplayNodeAssertThreadAffinity(self);
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
|
||||
if (_flags.shouldRasterizeDescendants == flag)
|
||||
if (_flags.shouldRasterizeDescendants == shouldRasterize)
|
||||
return;
|
||||
|
||||
_flags.shouldRasterizeDescendants = flag;
|
||||
_flags.shouldRasterizeDescendants = shouldRasterize;
|
||||
|
||||
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 __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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)contentsScaleForDisplay
|
||||
{
|
||||
@@ -969,7 +963,7 @@ static bool disableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASD
|
||||
[_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.
|
||||
if (!_flags.shouldRasterizeDescendants && ![self __rasterizedContainerNode]) {
|
||||
if (!_flags.shouldRasterizeDescendants && [self __shouldLoadViewOrLayer]) {
|
||||
if (_layer) {
|
||||
ASDisplayNodeCAssertMainThread();
|
||||
|
||||
@@ -1090,7 +1084,7 @@ static NSInteger incrementIfFound(NSInteger i) {
|
||||
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.
|
||||
if (!_flags.shouldRasterizeDescendants && ![self __rasterizedContainerNode]) {
|
||||
if (!_flags.shouldRasterizeDescendants && [self __shouldLoadViewOrLayer]) {
|
||||
if (_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");
|
||||
@@ -1345,7 +1339,15 @@ static NSInteger incrementIfFound(NSInteger i) {
|
||||
- (void)__setSupernode:(ASDisplayNode *)supernode
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (_supernode != supernode) {
|
||||
ASHierarchyState oldHierarchyState = _supernode.hierarchyState;
|
||||
_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.
|
||||
@@ -1653,7 +1655,8 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
* @see https://github.com/facebook/AsyncDisplayKit/issues/900
|
||||
* Possible solution is to push `isInCellNode` state downward on `addSubnode`/`removeFromSupernode`.
|
||||
*/
|
||||
- (BOOL)supportsInterfaceState {
|
||||
- (BOOL)supportsInterfaceState
|
||||
{
|
||||
return ([self isKindOfClass:ASCellNode.class]
|
||||
|| [self _supernodeWithClass:ASCellNode.class checkViewHierarchy:NO] != nil);
|
||||
}
|
||||
@@ -1664,23 +1667,23 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
return _interfaceState;
|
||||
}
|
||||
|
||||
- (void)setInterfaceState:(ASInterfaceState)interfaceState
|
||||
- (void)setInterfaceState:(ASInterfaceState)newState
|
||||
{
|
||||
ASInterfaceState oldValue;
|
||||
ASInterfaceState oldState;
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
oldValue = _interfaceState;
|
||||
_interfaceState = interfaceState;
|
||||
oldState = _interfaceState;
|
||||
_interfaceState = newState;
|
||||
}
|
||||
|
||||
if (interfaceState != oldValue) {
|
||||
if ((interfaceState & ASInterfaceStateMeasureLayout) != (oldValue & ASInterfaceStateMeasureLayout)) {
|
||||
if (newState != oldState) {
|
||||
if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) {
|
||||
// Trigger asynchronous measurement if it is not already cached or being calculated.
|
||||
}
|
||||
|
||||
// Entered or exited data loading state.
|
||||
if ((interfaceState & ASInterfaceStateFetchData) != (oldValue & ASInterfaceStateFetchData)) {
|
||||
if (interfaceState & ASInterfaceStateFetchData) {
|
||||
if ((newState & ASInterfaceStateFetchData) != (oldState & ASInterfaceStateFetchData)) {
|
||||
if (newState & ASInterfaceStateFetchData) {
|
||||
[self fetchData];
|
||||
} else {
|
||||
[self clearFetchedData];
|
||||
@@ -1688,8 +1691,8 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
}
|
||||
|
||||
// Entered or exited contents rendering state.
|
||||
if ((interfaceState & ASInterfaceStateDisplay) != (oldValue & ASInterfaceStateDisplay)) {
|
||||
if (interfaceState & ASInterfaceStateDisplay) {
|
||||
if ((newState & ASInterfaceStateDisplay) != (oldState & ASInterfaceStateDisplay)) {
|
||||
if (newState & ASInterfaceStateDisplay) {
|
||||
// Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here.
|
||||
[self setDisplaySuspended:NO];
|
||||
} else {
|
||||
@@ -1699,14 +1702,13 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
}
|
||||
|
||||
// Entered or exited data loading state.
|
||||
if ((interfaceState & ASInterfaceStateVisible) != (oldValue & ASInterfaceStateVisible)) {
|
||||
if (interfaceState & ASInterfaceStateVisible) {
|
||||
if ((newState & ASInterfaceStateVisible) != (oldState & ASInterfaceStateVisible)) {
|
||||
if (newState & ASInterfaceStateVisible) {
|
||||
// Consider providing a -didBecomeVisible.
|
||||
} else {
|
||||
// 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
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
@@ -37,6 +37,12 @@ extern ASDisplayNode *ASDisplayNodeUltimateParentOfNode(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.
|
||||
*/
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
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))
|
||||
{
|
||||
CALayer *layer = node.layer;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import "ASAvailability.h"
|
||||
#import "ASBaseDefines.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASLog.h"
|
||||
#import "ASPhotosFrameworkImageRequest.h"
|
||||
#import "ASEqualityHelpers.h"
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
|
||||
#import "ASBasicImageDownloader.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASThread.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASEqualityHelpers.h"
|
||||
#import "ASThread.h"
|
||||
|
||||
@interface ASNetworkImageNode ()
|
||||
{
|
||||
@@ -30,10 +31,8 @@
|
||||
|
||||
BOOL _imageLoaded;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ASNetworkImageNode
|
||||
|
||||
- (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASImageDownloaderProtocol>)downloader
|
||||
|
||||
@@ -10,16 +10,14 @@
|
||||
#import "ASTableViewInternal.h"
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "ASChangeSetDataController.h"
|
||||
#import "ASCollectionViewLayoutController.h"
|
||||
#import "ASLayoutController.h"
|
||||
#import "ASRangeController.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASLayout.h"
|
||||
|
||||
// FIXME: Temporary nonsense import until method names are finalized and exposed
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASLayoutController.h"
|
||||
#import "ASRangeController.h"
|
||||
|
||||
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
@@ -829,6 +827,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
- (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASCellNode *node = [_asyncDataSource tableView:self nodeForRowAtIndexPath:indexPath];
|
||||
[node enterHierarchyState:ASHierarchyStateRangeManaged];
|
||||
|
||||
ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode");
|
||||
if (node.layoutDelegate == nil) {
|
||||
node.layoutDelegate = self;
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
#import "ASViewController.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASDimension.h"
|
||||
|
||||
// FIXME: Temporary nonsense import until method names are finalized and exposed
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@implementation ASViewController
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import "ASRangeHandlerPreload.h"
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@implementation ASRangeHandlerPreload
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@interface ASRangeHandlerRender ()
|
||||
@property (nonatomic,readonly) UIWindow *workingWindow;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import "ASRangeHandlerVisible.h"
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@implementation ASRangeHandlerVisible
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#import "ASAssert.h"
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@implementation _ASDisplayLayer
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "ASAssert.h"
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
|
||||
@interface _ASDisplayView ()
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#import "_ASAsyncTransaction.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@implementation ASDisplayNode (AsyncDisplay)
|
||||
|
||||
@@ -84,7 +85,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
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 (rasterizingFromAscendent) {
|
||||
@@ -178,7 +179,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
|
||||
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) {
|
||||
CGRect bounds = self.bounds;
|
||||
@@ -296,7 +297,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
|
||||
if ([self __rasterizedContainerNode]) {
|
||||
if (_hierarchyState & ASHierarchyStateRasterized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
109
AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h
Normal file
109
AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h
Normal 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
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
#import "_ASCoreAnimationExtras.h"
|
||||
#import "_ASPendingState.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASEqualityHelpers.h"
|
||||
|
||||
/**
|
||||
@@ -219,9 +221,22 @@
|
||||
|
||||
- (void)setNeedsDisplay
|
||||
{
|
||||
ASDisplayNode *rasterizedContainerNode = [self __rasterizedContainerNode];
|
||||
if (rasterizedContainerNode) {
|
||||
if (_hierarchyState & ASHierarchyStateRasterized) {
|
||||
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];
|
||||
});
|
||||
} else {
|
||||
[_layer setNeedsDisplay];
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
|
||||
void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)());
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
|
||||
typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
|
||||
{
|
||||
ASDisplayNodeMethodOverrideNone = 0,
|
||||
ASDisplayNodeMethodOverrideTouchesBegan = 1 << 0,
|
||||
ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1,
|
||||
@@ -73,8 +74,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
|
||||
|
||||
_ASPendingState *_pendingViewState;
|
||||
|
||||
ASInterfaceState _interfaceState;
|
||||
|
||||
struct ASDisplayNodeFlags {
|
||||
// public properties
|
||||
unsigned synchronous:1;
|
||||
@@ -118,9 +117,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
|
||||
// Bitmask to check which methods an object overrides.
|
||||
@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
|
||||
- (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.
|
||||
- (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.
|
||||
- (id)initWithViewClass:(Class)viewClass;
|
||||
|
||||
@@ -160,12 +153,12 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
|
||||
|
||||
@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
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
{
|
||||
// If it's a space character and we have custom word kerning, use the whitespace action control character.
|
||||
if ([layoutManager.textStorage.string characterAtIndex:characterIndex] == ' ')
|
||||
return NSControlCharacterWhitespaceAction;
|
||||
return NSControlCharacterActionWhitespace;
|
||||
|
||||
return defaultAction;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user