Several bug fixes for the 1.0 release of AsyncDisplayKit

Fixes:
- Usage of UIScreen in -[ASDisplayNode init]
  (the offending individual will be prosecuted)
- #38: Proper teardown of nodes owned by ASTableView / ASRangeController
- #34: Fix infinite recursion in very rare subclassing scenario
- #30: Avoid animating cell and section additions to ASTableView
- #19: Set a more reasonable default for maximum display concurrency

r=nadi
This commit is contained in:
Scott Goodson 2014-10-14 20:53:25 -07:00
parent f7d91bb877
commit c61b1c294c
4 changed files with 74 additions and 41 deletions

View File

@ -47,6 +47,22 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
return (superclassIMP != subclassIMP);
}
CGFloat ASDisplayNodeScreenScale()
{
static CGFloat screenScale = 0.0;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if ([NSThread isMainThread]) {
screenScale = [[UIScreen mainScreen] scale];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
screenScale = [[UIScreen mainScreen] scale];
});
}
});
return screenScale;
}
+ (void)initialize
{
if (self == [ASDisplayNode class]) {
@ -74,14 +90,44 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
return [_ASDisplayLayer class];
}
#pragma mark - NSObject Overrides
#pragma mark - Lifecycle
// Avoid recursive loops if a subclass implements an init method that calls -initWith*Class:
- (void)_initializeInstance
{
_contentsScaleForDisplay = ASDisplayNodeScreenScale();
_displaySentinel = [[ASSentinel alloc] init];
_flags.inWindow = NO;
_flags.displaysAsynchronously = YES;
// As an optimization, it may be worth a caching system that performs these checks once per class in +initialize (see above).
_flags.implementsDisplay = [[self class] respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] || [self.class respondsToSelector:@selector(displayWithParameters:isCancelled:)];
_flags.hasClassDisplay = ([[self class] respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
_flags.hasWillDisplayAsyncLayer = ([self respondsToSelector:@selector(willDisplayAsyncLayer:)] ? 1 : 0);
_flags.hasDrawParametersForAsyncLayer = ([self respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
}
- (id)init
{
if (!(self = [super init]))
return nil;
[self _initializeInstance];
return self;
}
- (id)initWithViewClass:(Class)viewClass
{
if (!(self = [self init]))
if (!(self = [super init]))
return nil;
ASDisplayNodeAssert([viewClass isSubclassOfClass:[UIView class]], @"should initialize with a subclass of UIView");
[self _initializeInstance];
_viewClass = viewClass;
_flags.isSynchronous = ![viewClass isSubclassOfClass:[_ASDisplayView class]];
@ -90,40 +136,19 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
- (id)initWithLayerClass:(Class)layerClass
{
if (!(self = [self init]))
if (!(self = [super init]))
return nil;
ASDisplayNodeAssert([layerClass isSubclassOfClass:[CALayer class]], @"should initialize with a subclass of CALayer");
[self _initializeInstance];
_layerClass = layerClass;
_flags.isSynchronous = ![layerClass isSubclassOfClass:[_ASDisplayLayer class]];
_flags.isLayerBacked = YES;
return self;
}
- (id)init
{
self = [super init];
if (!self) return nil;
_contentsScaleForDisplay = [[UIScreen mainScreen] scale];
_displaySentinel = [[ASSentinel alloc] init];
_flags.inWindow = NO;
_flags.displaysAsynchronously = YES;
_flags.implementsDisplay = [[self class] respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] || [self.class respondsToSelector:@selector(displayWithParameters:isCancelled:)];
_flags.hasWillDisplayAsyncLayer = ([self respondsToSelector:@selector(willDisplayAsyncLayer:)] ? 1 : 0);
_flags.hasClassDisplay = ([[self class] respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
_flags.hasDrawParametersForAsyncLayer = ([self respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
return self;
}
- (void)dealloc
{
ASDisplayNodeAssertMainThread();
@ -142,9 +167,8 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
_view = nil;
_subnodes = nil;
if (_flags.isLayerBacked) {
if (_flags.isLayerBacked)
_layer.delegate = nil;
}
_layer = nil;
[self __setSupernode:nil];

View File

@ -355,10 +355,10 @@ static BOOL _isInterceptedSelector(SEL sel)
if (newSectionCount > sectionCount) {
NSRange range = NSMakeRange(sectionCount, newSectionCount - sectionCount);
NSIndexSet *sections = [NSIndexSet indexSetWithIndexesInRange:range];
[super insertSections:sections withRowAnimation:UITableViewRowAnimationAutomatic];
[super insertSections:sections withRowAnimation:UITableViewRowAnimationNone];
}
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[super insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[super endUpdates];
}

View File

@ -65,6 +65,22 @@ typedef NS_ENUM(NSInteger, ASScrollDirection) {
return self;
}
- (void)dealloc
{
[self teardownAllNodes];
}
- (void)teardownAllNodes
{
for (ASCellNode *node in _nodes.allValues) {
[node removeFromSupernode];
[node.view removeFromSuperview];
}
[_nodes removeAllObjects];
_nodes = nil;
}
+ (dispatch_queue_t)sizingQueue
{
static dispatch_queue_t sizingQueue = NULL;
@ -255,16 +271,7 @@ static BOOL ASRangeIsValid(NSRange range)
/*
* teardown
*/
for (ASCellNode *node in _nodes.objectEnumerator) {
[node removeFromSupernode];
[node.view removeFromSuperview];
}
[_nodes removeAllObjects];
_nodes = nil;
for (UIView *view in [[ASRangeController workingView] subviews]) {
[view removeFromSuperview];
}
[self teardownAllNodes];
/*
* setup

View File

@ -23,7 +23,9 @@
#if ASDISPLAYNODE_DELAY_DISPLAY
static long __ASDisplayLayerMaxConcurrentDisplayCount = 1;
#else
static long __ASDisplayLayerMaxConcurrentDisplayCount = 1024; // essentially no limit until we determine a good value.
// Basing this off of CPU core count would make sense, but first some experimentation should be done to understand
// if having more ready-to-run work keeps the CPU clock up (or other interesting scheduler effects).
static long __ASDisplayLayerMaxConcurrentDisplayCount = 8;
#endif
static dispatch_semaphore_t __ASDisplayLayerConcurrentDisplaySemaphore;