diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index b3f9b0ca83..a3399b0402 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -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]; diff --git a/AsyncDisplayKit/ASTableView.m b/AsyncDisplayKit/ASTableView.m index c33feba12f..c4a0673fb5 100644 --- a/AsyncDisplayKit/ASTableView.m +++ b/AsyncDisplayKit/ASTableView.m @@ -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]; } diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 5bc3390cfd..d74a974597 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -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 diff --git a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm index 7b14fe92ed..178145e9ac 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm @@ -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;