Files
AsyncDisplayKit/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m
Michael Schneider d5a7c19522 [ASLayoutSpec] Use childrenMap directly to prevent creating an NSArray within ASDK Part 2 (#2021)
* Revert "Revert "[ASLayoutSpec] Use childrenMap directly to prevent creating an NSArray within ASDK (#1937)""

This reverts commit 735b4ebd08.

* Fix crash and add exception for mutating while using fast enumeration of ASLayoutSpec children

NSFastEnumeration is potentially quite dangerous in the wrong hands. In particular, it does not provide a safe mechanism for you to return temporary objects directly, and it does not provide any guarantee that you will be called when the enumeration has completed; therefore if we generate temporaries and store them in an instance variable, we will not necessarily be able to clean them up! This means fast enumeration methods should never be called within an autorelease pool or the autorelease pool be drained within the fast enumeration loop.

The reason is we store references to objects in the stackBuf struct by casting the child pointer to __autoreleasing id. If we pop the autorelease pool between calls to -countByEnumeratingWithState:objects:count:, it will die in a messy explosion of pointer dereferences and EXC_BAD_ACCESS.

* Add tests for ASDisplayNode and ASLayoutSpec fast enumeration
2016-08-01 18:08:48 -07:00

105 lines
2.5 KiB
Objective-C

//
// ASLayoutSpecSnapshotTestsHelper.m
// AsyncDisplayKit
//
// 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.
//
#import "ASLayoutSpecSnapshotTestsHelper.h"
#import "ASDisplayNode.h"
#import "ASLayoutSpec.h"
#import "ASLayout.h"
@interface ASTestNode : ASDisplayNode
- (void)setLayoutSpecUnderTest:(ASLayoutSpec *)layoutSpecUnderTest sizeRange:(ASSizeRange)sizeRange;
@end
@implementation ASLayoutSpecSnapshotTestCase
- (void)setUp
{
[super setUp];
self.recordMode = NO;
}
- (void)testLayoutSpec:(ASLayoutSpec *)layoutSpec
sizeRange:(ASSizeRange)sizeRange
subnodes:(NSArray *)subnodes
identifier:(NSString *)identifier
{
ASTestNode *node = [[ASTestNode alloc] init];
for (ASDisplayNode *subnode in subnodes) {
[node addSubnode:subnode];
}
[node setLayoutSpecUnderTest:layoutSpec sizeRange:sizeRange];
ASSnapshotVerifyNode(node, identifier);
}
- (void)testFastEnumeration
{
ASLayoutSpec *layoutSpec = [[ASLayoutSpec alloc] init];
NSMutableArray *children = [NSMutableArray array];
for (int i = 0; i < 100; i++) {
[children addObject:[[ASDisplayNode alloc] init]];
}
layoutSpec.children = children;
NSInteger i = 0;
for (ASDisplayNode *child in layoutSpec) {
XCTAssertEqualObjects(child, children[i]);
i++;
}
}
@end
@implementation ASTestNode
{
ASLayout *_layoutUnderTest;
}
- (instancetype)init
{
if (self = [super init]) {
self.layerBacked = YES;
}
return self;
}
- (void)setLayoutSpecUnderTest:(ASLayoutSpec *)layoutSpecUnderTest sizeRange:(ASSizeRange)sizeRange
{
ASLayout *layout = [layoutSpecUnderTest measureWithSizeRange:sizeRange];
layout.position = CGPointZero;
layout = [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:sizeRange
size:layout.size
sublayouts:@[layout]];
_layoutUnderTest = [layout filteredNodeLayoutTree];
self.frame = CGRectMake(0, 0, _layoutUnderTest.size.width, _layoutUnderTest.size.height);
[self measure:_layoutUnderTest.size];
}
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
return _layoutUnderTest;
}
@end
@implementation ASStaticSizeDisplayNode
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
return _staticSize;
}
@end