diff --git a/Source/ASDisplayNode+Yoga.mm b/Source/ASDisplayNode+Yoga.mm index 93615cdd6..235db6678 100644 --- a/Source/ASDisplayNode+Yoga.mm +++ b/Source/ASDisplayNode+Yoga.mm @@ -35,7 +35,7 @@ @implementation ASDisplayNode (Yoga) - (ASDisplayNode *)yogaRoot { ASDisplayNode *yogaRoot = self; - ASDisplayNode *yogaParent = nil; + ASDisplayNode *yogaParent = nil; while ((yogaParent = yogaRoot.yogaParent)) { yogaRoot = yogaParent; } diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 24adf9414..75604bc99 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -3280,11 +3280,13 @@ - (void)didEnterVisibleState // subclass override ASDisplayNodeAssertMainThread(); +#if ASDISPLAYNODE_ASSERTIONS_ENABLED // Rasterized node's loading state is merged with root node of rasterized tree. if (!(self.hierarchyState & ASHierarchyStateRasterized)) { ASDisplayNodeAssert(self.isNodeLoaded, @"Node should be loaded before entering visible state."); } - +#endif + ASAssertUnlocked(__instanceLock__); [self enumerateInterfaceStateDelegates:^(id del) { [del didEnterVisibleState]; diff --git a/Tests/ASButtonNodeTests.m b/Tests/ASButtonNodeTests.m index 1c0c0adb6..eb6249e45 100644 --- a/Tests/ASButtonNodeTests.m +++ b/Tests/ASButtonNodeTests.m @@ -41,8 +41,7 @@ - (void)testAccessibility // Disable the button and verify that accessibility traits has been updated correctly. buttonNode.enabled = NO; - UIAccessibilityTraits disabledButtonTrait = - UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled; + UIAccessibilityTraits disabledButtonTrait = UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled; XCTAssertTrue(buttonNode.accessibilityTraits == disabledButtonTrait, @"Should have disabled button accessibility trait, instead has %llu", buttonNode.accessibilityTraits); diff --git a/Tests/ASTextNodeTests.m b/Tests/ASTextNodeTests.m index daf8cf7d7..441ca8f10 100644 --- a/Tests/ASTextNodeTests.m +++ b/Tests/ASTextNodeTests.m @@ -50,6 +50,7 @@ @interface ASTextNodeTests : XCTestCase @property (nonatomic) ASTextNode *textNode; @property (nonatomic, copy) NSAttributedString *attributedText; +@property (nonatomic) NSMutableArray *textNodeBucket; @end @@ -59,6 +60,7 @@ - (void)setUp { [super setUp]; _textNode = [[ASTextNode alloc] init]; + _textNodeBucket = [[NSMutableArray alloc] init]; UIFontDescriptor *desc = [UIFontDescriptor fontDescriptorWithName:@"Didot" size:18]; @@ -247,6 +249,61 @@ - (void)testThatTheExperimentWorksCorrectly XCTAssertEqualObjects(sc2.superclass, [ASTextNodeSubclass class]); } +- (void)testTextNodeSwitchWorksInMultiThreadEnvironment +{ + ASConfiguration *config = [ASConfiguration new]; + config.experimentalFeatures = ASExperimentalTextNode; + [ASConfigurationManager test_resetWithConfiguration:config]; + XCTestExpectation *exp = [self expectationWithDescription:@"wait for full bucket"]; + + dispatch_queue_t queue = dispatch_queue_create("com.texture.AsyncDisplayKit.ASTextNodeTestsQueue", DISPATCH_QUEUE_CONCURRENT); + dispatch_group_t g = dispatch_group_create(); + for (int i = 0; i < 20; i++) { + dispatch_group_async(g, queue, ^{ + ASTextNode *textNode = [[ASTextNodeSecondSubclass alloc] init]; + XCTAssert([textNode isKindOfClass:[ASTextNode2 class]]); + @synchronized(self.textNodeBucket) { + [self.textNodeBucket addObject:textNode]; + if (self.textNodeBucket.count == 20) { + [exp fulfill]; + } + } + }); + } + [self waitForExpectations:@[exp] timeout:3]; + exp = nil; + [self.textNodeBucket removeAllObjects]; +} + +- (void)testTextNodeSwitchWorksInMultiThreadEnvironment2 +{ + ASConfiguration *config = [ASConfiguration new]; + config.experimentalFeatures = ASExperimentalTextNode; + [ASConfigurationManager test_resetWithConfiguration:config]; + XCTestExpectation *exp = [self expectationWithDescription:@"wait for full bucket"]; + + NSLock *lock = [[NSLock alloc] init]; + NSMutableArray *textNodeBucket = [[NSMutableArray alloc] init]; + + dispatch_queue_t queue = dispatch_queue_create("com.texture.AsyncDisplayKit.ASTextNodeTestsQueue", DISPATCH_QUEUE_CONCURRENT); + dispatch_group_t g = dispatch_group_create(); + for (int i = 0; i < 20; i++) { + dispatch_group_async(g, queue, ^{ + ASTextNode *textNode = [[ASTextNodeSecondSubclass alloc] init]; + XCTAssert([textNode isKindOfClass:[ASTextNode2 class]]); + [lock lock]; + [textNodeBucket addObject:textNode]; + if (textNodeBucket.count == 20) { + [exp fulfill]; + } + [lock unlock]; + }); + } + [self waitForExpectations:@[exp] timeout:3]; + exp = nil; + [textNodeBucket removeAllObjects]; +} + @end @implementation ASTextNodeSubclass