Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 841bed6

Browse files
committed
Merge pull request #962 from facebook/OptimizeVisibilityNotificationDisablement
[ASDisplayNode] Order-of-magnitude speedup in handling of "disable visibility" notifications.
2 parents b787020 + b8602d1 commit 841bed6

8 files changed

Lines changed: 42 additions & 31 deletions

AsyncDisplayKit/ASDisplayNode.mm

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,42 +1253,46 @@ - (void)removeFromSupernode
12531253

12541254
- (BOOL)__visibilityNotificationsDisabled
12551255
{
1256+
// Currently, this method is only used by the testing infrastructure to verify this internal feature.
12561257
ASDN::MutexLocker l(_propertyLock);
12571258
return _flags.visibilityNotificationsDisabled > 0;
12581259
}
12591260

1261+
- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled
1262+
{
1263+
ASDN::MutexLocker l(_propertyLock);
1264+
return (_hierarchyState & ASHierarchyStateTransitioningSupernodes);
1265+
}
1266+
12601267
- (void)__incrementVisibilityNotificationsDisabled
12611268
{
12621269
ASDN::MutexLocker l(_propertyLock);
12631270
const size_t maxVisibilityIncrement = (1ULL<<VISIBILITY_NOTIFICATIONS_DISABLED_BITS) - 1ULL;
12641271
ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled < maxVisibilityIncrement, @"Oops, too many increments of the visibility notifications API");
1265-
if (_flags.visibilityNotificationsDisabled < maxVisibilityIncrement)
1272+
if (_flags.visibilityNotificationsDisabled < maxVisibilityIncrement) {
12661273
_flags.visibilityNotificationsDisabled++;
1274+
}
1275+
if (_flags.visibilityNotificationsDisabled == 1) {
1276+
// Must have just transitioned from 0 to 1. Notify all subnodes that we are in a disabled state.
1277+
[self enterHierarchyState:ASHierarchyStateTransitioningSupernodes];
1278+
}
12671279
}
12681280

12691281
- (void)__decrementVisibilityNotificationsDisabled
12701282
{
12711283
ASDN::MutexLocker l(_propertyLock);
12721284
ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled > 0, @"Can't decrement past 0");
1273-
if (_flags.visibilityNotificationsDisabled > 0)
1285+
if (_flags.visibilityNotificationsDisabled > 0) {
12741286
_flags.visibilityNotificationsDisabled--;
1275-
}
1276-
1277-
// This uses the layer hieararchy for safety. Who knows what people might do and it would be bad to have visibilty out of sync
1278-
- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled
1279-
{
1280-
CALayer *layer = _layer;
1281-
do {
1282-
ASDisplayNode *node = ASLayerToDisplayNode(layer);
1283-
if (node) {
1284-
if (node->_flags.visibilityNotificationsDisabled) {
1285-
return YES;
1286-
}
1287-
}
1288-
layer = layer.superlayer;
1289-
} while (layer);
1290-
1291-
return NO;
1287+
}
1288+
if (_flags.visibilityNotificationsDisabled == 0) {
1289+
// Must have just transitioned from 1 to 0. Notify all subnodes that we are no longer in a disabled state.
1290+
// FIXME: This system should be revisited when refactoring and consolidating the implementation of the
1291+
// addSubnode: and insertSubnode:... methods. As implemented, though logically irrelevant for expected use cases,
1292+
// multiple nodes in the subtree below may have a non-zero visibilityNotification count and still have
1293+
// the ASHierarchyState bit cleared (the only value checked when reading this state).
1294+
[self exitHierarchyState:ASHierarchyStateTransitioningSupernodes];
1295+
}
12921296
}
12931297

12941298
- (void)__enterHierarchy

AsyncDisplayKit/Details/ASBasicImageDownloader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@
1616

1717
+ (instancetype)sharedImageDownloader;
1818

19+
+ (instancetype)new __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used.")));
20+
- (instancetype)init __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used.")));
21+
1922
@end

AsyncDisplayKit/Details/ASBasicImageDownloader.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,14 @@ + (instancetype)sharedImageDownloader
205205
static ASBasicImageDownloader *sharedImageDownloader = nil;
206206
static dispatch_once_t once = 0;
207207
dispatch_once(&once, ^{
208-
sharedImageDownloader = [[ASBasicImageDownloader alloc] init];
208+
sharedImageDownloader = [[ASBasicImageDownloader alloc] _init];
209209
});
210210
return sharedImageDownloader;
211211
}
212212

213213
#pragma mark Lifecycle.
214214

215-
- (instancetype)init
215+
- (instancetype)_init
216216
{
217217
if (!(self = [super init]))
218218
return nil;

AsyncDisplayKit/Private/ASDisplayNode+DebugTiming.mm

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@
77
*/
88

99
#import "ASDisplayNode+DebugTiming.h"
10-
1110
#import "ASDisplayNodeInternal.h"
1211

1312
@implementation ASDisplayNode (DebugTiming)
1413

15-
1614
#if TIME_DISPLAYNODE_OPS
1715
- (NSTimeInterval)debugTimeToCreateView
1816
{
@@ -83,6 +81,4 @@ - (NSTimeInterval)debugAllCreationTime
8381

8482
#endif
8583

86-
87-
8884
@end

AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,17 @@
3737
typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
3838
{
3939
/** The node may or may not have a supernode, but no supernode has a special hierarchy-influencing option enabled. */
40-
ASHierarchyStateNormal = 0,
40+
ASHierarchyStateNormal = 0,
4141
/** The node has a supernode with .shouldRasterizeDescendants = YES.
4242
Note: the root node of the rasterized subtree (the one with the property set on it) will NOT have this state set. */
43-
ASHierarchyStateRasterized = 1 << 0,
43+
ASHierarchyStateRasterized = 1 << 0,
4444
/** The node or one of its supernodes is managed by a class like ASRangeController. Most commonly, these nodes are
4545
ASCellNode objects or a subnode of one, and are used in ASTableView or ASCollectionView.
4646
These nodes also recieve regular updates to the .interfaceState property with more detailed status information. */
47-
ASHierarchyStateRangeManaged = 1 << 1,
47+
ASHierarchyStateRangeManaged = 1 << 1,
48+
/** Down-propogated version of _flags.visibilityNotificationsDisabled. This flag is very rarely set, but by having it
49+
locally available to nodes, they do not have to walk up supernodes at the critical points it is checked. */
50+
ASHierarchyStateTransitioningSupernodes = 1 << 2
4851
};
4952

5053
@interface ASDisplayNode () <_ASDisplayLayerDelegate>

AsyncDisplayKit/Private/ASDisplayNodeInternal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
105105
NSTimeInterval _debugTimeToAddSubnodeViews;
106106
NSTimeInterval _debugTimeForDidLoad;
107107
#endif
108-
109108
}
110109

111110
+ (void)scheduleNodeForDisplay:(ASDisplayNode *)node;
@@ -136,6 +135,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
136135

137136
// Private API for helper functions / unit tests. Use ASDisplayNodeDisableHierarchyNotifications() to control this.
138137
- (BOOL)__visibilityNotificationsDisabled;
138+
- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled;
139139
- (void)__incrementVisibilityNotificationsDisabled;
140140
- (void)__decrementVisibilityNotificationsDisabled;
141141

AsyncDisplayKitTests/ASBasicImageDownloaderTests.m

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ @interface ASBasicImageDownloaderTests : XCTestCase
1616

1717
@implementation ASBasicImageDownloaderTests
1818

19-
- (void)testAsynchronouslyDownloadTheSameURLTwice {
20-
ASBasicImageDownloader *downloader = [ASBasicImageDownloader new];
19+
- (void)testAsynchronouslyDownloadTheSameURLTwice
20+
{
21+
ASBasicImageDownloader *downloader = [ASBasicImageDownloader sharedImageDownloader];
2122

2223
NSURL *URL = [NSURL URLWithString:@"http://wrongPath/wrongResource.png"];
2324

AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C
4949

5050
@interface ASDisplayNode (PrivateStuffSoWeDontPullInCPPInternalH)
5151
- (BOOL)__visibilityNotificationsDisabled;
52+
- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled;
5253
- (id)initWithViewClass:(Class)viewClass;
5354
- (id)initWithLayerClass:(Class)layerClass;
5455
@end
@@ -360,6 +361,7 @@ - (void)checkMoveAcrossHierarchyLayerBacked:(BOOL)isLayerBacked useManualCalls:(
360361
}
361362
if (useManualDisable) {
362363
XCTAssertTrue([child __visibilityNotificationsDisabled], @"Should not have re-enabled yet");
364+
XCTAssertTrue([child __selfOrParentHasVisibilityNotificationsDisabled], @"Should not have re-enabled yet");
363365
ASDisplayNodeEnableHierarchyNotifications(child);
364366
}
365367

@@ -377,6 +379,7 @@ - (void)checkMoveAcrossHierarchyLayerBacked:(BOOL)isLayerBacked useManualCalls:(
377379
}
378380
if (useManualDisable) {
379381
XCTAssertTrue([child __visibilityNotificationsDisabled], @"Should not have re-enabled yet");
382+
XCTAssertTrue([child __selfOrParentHasVisibilityNotificationsDisabled], @"Should not have re-enabled yet");
380383
ASDisplayNodeEnableHierarchyNotifications(child);
381384
}
382385

@@ -390,6 +393,7 @@ - (void)checkMoveAcrossHierarchyLayerBacked:(BOOL)isLayerBacked useManualCalls:(
390393

391394
// Make sure that we don't leave these unbalanced
392395
XCTAssertFalse([child __visibilityNotificationsDisabled], @"Unbalanced visibility notifications calls");
396+
XCTAssertFalse([child __selfOrParentHasVisibilityNotificationsDisabled], @"Should not have re-enabled yet");
393397

394398
[window release];
395399
}

0 commit comments

Comments
 (0)