diff --git a/AsyncDisplayKit.podspec b/AsyncDisplayKit.podspec index a3b062d43d..f23bdaa276 100644 --- a/AsyncDisplayKit.podspec +++ b/AsyncDisplayKit.podspec @@ -30,12 +30,6 @@ Pod::Spec.new do |spec| 'AsyncDisplayKit/TextKit/ASTextKitComponents.h' ] - # ASDealloc2MainObject must be compiled with MRR - core.exclude_files = [ - 'AsyncDisplayKit/Private/_AS-objc-internal.h', - 'AsyncDisplayKit/Details/ASDealloc2MainObject.h', - 'AsyncDisplayKit/Details/ASDealloc2MainObject.m', - ] core.source_files = [ 'AsyncDisplayKit/**/*.{h,m,mm}', 'Base/*.{h,m}', @@ -46,21 +40,11 @@ Pod::Spec.new do |spec| # See https://github.com/facebook/AsyncDisplayKit/issues/1153 'AsyncDisplayKit/TextKit/*.h', ] - core.dependency 'AsyncDisplayKit/ASDealloc2MainObject' - end - - spec.subspec 'ASDealloc2MainObject' do |mrr| - mrr.requires_arc = false - mrr.source_files = [ - 'AsyncDisplayKit/Private/_AS-objc-internal.h', - 'AsyncDisplayKit/Details/ASDealloc2MainObject.h', - 'AsyncDisplayKit/Details/ASDealloc2MainObject.m', - ] end spec.subspec 'PINRemoteImage' do |pin| pin.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) PIN_REMOTE_IMAGE=1' } - pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.6' + pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.7' pin.dependency 'PINRemoteImage/PINCache' pin.dependency 'AsyncDisplayKit/Core' end diff --git a/AsyncDisplayKit/ASCollectionNode.mm b/AsyncDisplayKit/ASCollectionNode.mm index 7f4da85d04..7157480893 100644 --- a/AsyncDisplayKit/ASCollectionNode.mm +++ b/AsyncDisplayKit/ASCollectionNode.mm @@ -125,7 +125,8 @@ { __weak __typeof__(self) weakSelf = self; ASDisplayNodeViewBlock collectionViewBlock = ^UIView *{ - __typeof__(self) strongSelf = weakSelf; + // Variable will be unused if event logging is off. + __unused __typeof__(self) strongSelf = weakSelf; return [[ASCollectionView alloc] _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:layoutFacilitator eventLog:ASDisplayNodeGetEventLog(strongSelf)]; }; diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 2cf78a6f58..78edb47237 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -360,6 +360,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [self reloadDataWithCompletion:nil]; } +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + if ([self validateIndexPath:indexPath]) { + [super scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } +} + - (void)reloadDataImmediately { ASDisplayNodeAssertMainThread(); @@ -622,8 +629,35 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; } } +/** + * Asserts that the index path is a valid view-index-path, and returns it if so, nil otherwise. + */ +- (nullable NSIndexPath *)validateIndexPath:(nullable NSIndexPath *)indexPath +{ + if (indexPath == nil) { + return nil; + } + + NSInteger section = indexPath.section; + if (section >= self.numberOfSections) { + ASDisplayNodeFailAssert(@"Collection view index path has invalid section %lu, section count = %lu", (unsigned long)section, (unsigned long)self.numberOfSections); + return nil; + } + + if (indexPath.item >= [self numberOfItemsInSection:section]) { + ASDisplayNodeFailAssert(@"Collection view index path has invalid item %lu in section %lu, item count = %lu", (unsigned long)indexPath.item, (unsigned long)section, (unsigned long)[self numberOfItemsInSection:section]); + return nil; + } + + return indexPath; +} + - (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath { + if ([self validateIndexPath:indexPath] == nil) { + return nil; + } + // If this is a section index path, we don't currently have a method // to do a mapping. if (indexPath.item == NSNotFound) { @@ -658,7 +692,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode { - return [_dataController completedIndexPathForNode:cellNode]; + return [self validateIndexPath:[_dataController completedIndexPathForNode:cellNode]]; } - (NSArray *)visibleNodes @@ -1710,6 +1744,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; [_rangeController setNeedsUpdate]; [_rangeController updateIfNeeded]; } + + // When we aren't visible, we will only fetch up to the visible area. Now that we are visible, + // we will fetch visible area + leading screens, so we need to check. + if (visible) { + [self _checkForBatchFetching]; + } } #pragma mark ASCALayerExtendedDelegate diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 5f00e29867..d5659303ca 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -893,6 +893,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) { ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self); + // Our calculated layout is suitable for this constrainedSize, so keep using it and + // invalidate any pending layout that has been generated in the past. + _pendingDisplayNodeLayout = nullptr; return _calculatedDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}]; } @@ -2990,12 +2993,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) - (void)didExitPreloadState { if (_methodOverrides & ASDisplayNodeMethodOverrideClearFetchedData) { - if ([self supportsRangeManagedInterfaceState]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [self clearFetchedData]; #pragma clang diagnostic pop - } } } @@ -3065,7 +3066,11 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) if (nowPreload) { [self didEnterPreloadState]; } else { - [self didExitPreloadState]; + // We don't want to call -didExitPreloadState on nodes that aren't being managed by a range controller. + // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop. + if ([self supportsRangeManagedInterfaceState]) { + [self didExitPreloadState]; + } } } diff --git a/AsyncDisplayKit/ASTableNode.mm b/AsyncDisplayKit/ASTableNode.mm index 10e54c38e4..4d65597720 100644 --- a/AsyncDisplayKit/ASTableNode.mm +++ b/AsyncDisplayKit/ASTableNode.mm @@ -83,7 +83,8 @@ { __weak __typeof__(self) weakSelf = self; ASDisplayNodeViewBlock tableViewBlock = ^UIView *{ - __typeof__(self) strongSelf = weakSelf; + // Variable will be unused if event logging is off. + __unused __typeof__(self) strongSelf = weakSelf; return [[ASTableView alloc] _initWithFrame:frame style:style dataControllerClass:dataControllerClass eventLog:ASDisplayNodeGetEventLog(strongSelf)]; }; diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 1465e1125c..b98b7a7ec4 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -469,6 +469,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [super reloadData]; } +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + if ([self validateIndexPath:indexPath]) { + [super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } +} + - (void)relayoutItems { [_dataController relayoutAllNodes]; @@ -523,6 +530,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath { + if ([self validateIndexPath:indexPath] == nil) { + return nil; + } + // If this is a section index path, we don't currently have a method // to do a mapping. if (indexPath.row == NSNotFound) { @@ -555,12 +566,37 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return [self indexPathForNode:cellNode waitingIfNeeded:NO]; } +/** + * Asserts that the index path is a valid view-index-path, and returns it if so, nil otherwise. + */ +- (nullable NSIndexPath *)validateIndexPath:(nullable NSIndexPath *)indexPath +{ + if (indexPath == nil) { + return nil; + } + + NSInteger section = indexPath.section; + if (section >= self.numberOfSections) { + ASDisplayNodeFailAssert(@"Table view index path has invalid section %lu, section count = %lu", (unsigned long)section, (unsigned long)self.numberOfSections); + return nil; + } + + if (indexPath.item >= [self numberOfRowsInSection:section]) { + ASDisplayNodeFailAssert(@"Table view index path has invalid item %lu in section %lu, item count = %lu", (unsigned long)indexPath.item, (unsigned long)section, (unsigned long)[self numberOfRowsInSection:section]); + return nil; + } + + return indexPath; +} + - (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait { NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode]; + indexPath = [self validateIndexPath:indexPath]; if (indexPath == nil && wait) { [_dataController waitUntilAllUpdatesAreCommitted]; indexPath = [_dataController completedIndexPathForNode:cellNode]; + indexPath = [self validateIndexPath:indexPath]; } return indexPath; } @@ -1727,6 +1763,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [_rangeController setNeedsUpdate]; [_rangeController updateIfNeeded]; } + + // When we aren't visible, we will only fetch up to the visible area. Now that we are visible, + // we will fetch visible area + leading screens, so we need to check. + if (visible) { + [self _checkForBatchFetching]; + } } @end diff --git a/AsyncDisplayKit/Details/ASCollectionInternal.h b/AsyncDisplayKit/Details/ASCollectionInternal.h index 3eb7f02ada..abb7e94ef1 100644 --- a/AsyncDisplayKit/Details/ASCollectionInternal.h +++ b/AsyncDisplayKit/Details/ASCollectionInternal.h @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN @class ASRangeController; @interface ASCollectionView () -- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator eventLog:(ASEventLog*)eventLog; +- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator eventLog:(nullable ASEventLog *)eventLog; @property (nonatomic, weak, readwrite) ASCollectionNode *collectionNode; @property (nonatomic, strong, readonly) ASDataController *dataController; diff --git a/AsyncDisplayKit/Private/ASBatchFetching.h b/AsyncDisplayKit/Private/ASBatchFetching.h index 6eca8a5940..ffc679d976 100644 --- a/AsyncDisplayKit/Private/ASBatchFetching.h +++ b/AsyncDisplayKit/Private/ASBatchFetching.h @@ -47,6 +47,7 @@ BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView_itemCounts = {0}; + [self _primitiveBatchFetchingFillTestAnimated:NO visible:NO controller:ctrl]; + XCTAssertGreaterThan([ctrl.collectionNode numberOfItemsInSection:0], 0); + [self _primitiveBatchFetchingFillTestAnimated:NO visible:YES controller:ctrl]; +} + +- (void)_primitiveBatchFetchingFillTestAnimated:(BOOL)animated visible:(BOOL)visible controller:(nullable ASCollectionViewTestController *)testController +{ + if (testController == nil) { + testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; + // Start with 1 empty section + testController.asyncDelegate->_itemCounts = {0}; + } + ASCollectionNode *cn = testController.collectionNode; + + UIWindow *window = nil; + UIView *view = nil; + if (visible) { + window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + view = window; + } else { + view = cn.view; + view.frame = [UIScreen mainScreen].bounds; + } + + XCTestExpectation *expectation = [self expectationWithDescription:@"Completed all batch fetches"]; + __weak ASCollectionViewTestController *weakController = testController; + __block NSInteger batchFetchCount = 0; + testController.asyncDelegate.willBeginBatchFetch = ^(ASBatchContext *context) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSInteger fetchIndex = batchFetchCount++; + + NSInteger itemCount = weakController.asyncDelegate->_itemCounts[0]; + weakController.asyncDelegate->_itemCounts[0] = (itemCount + 1); + if (animated) { + [cn insertItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:itemCount inSection:0] ]]; + } else { + [cn performBatchAnimated:NO updates:^{ + [cn insertItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:itemCount inSection:0] ]]; + } completion:nil]; + } + + [context completeBatchFetching:YES]; + + // If no more batch fetches have happened in 1 second, assume we're done. + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if (fetchIndex == batchFetchCount - 1) { + [expectation fulfill]; + } + }); + }); + }; + window.rootViewController = testController; + + [window makeKeyAndVisible]; + [view layoutIfNeeded]; + + [self waitForExpectationsWithTimeout:60 handler:nil]; + CGFloat contentHeight = cn.view.contentSize.height; + CGFloat requiredContentHeight; + CGFloat itemHeight = [cn.view layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]].size.height; + if (visible) { + requiredContentHeight = CGRectGetMaxY(cn.bounds) + CGRectGetHeight(cn.bounds) * cn.view.leadingScreensForBatching; + } else { + requiredContentHeight = CGRectGetMaxY(cn.bounds); + } + XCTAssertGreaterThan(batchFetchCount, 2); + XCTAssertGreaterThanOrEqual(contentHeight, requiredContentHeight, @"Loaded too little content."); + XCTAssertLessThanOrEqual(contentHeight, requiredContentHeight + 2 * itemHeight, @"Loaded too much content."); +} + @end diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 3494d00917..4cafa0e9a5 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -1941,6 +1941,7 @@ static bool stringContainsPointer(NSString *description, id p) { - (void)testDidExitPreloadIsCalledWhenNodesExitPreloadRange { ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init]; + [node setHierarchyState:ASHierarchyStateRangeManaged]; [node recursivelySetInterfaceState:ASInterfaceStatePreload]; [node recursivelySetInterfaceState:ASInterfaceStateDisplay]; diff --git a/Base/ASAssert.h b/Base/ASAssert.h index 2df66b37a7..a4079c4ffc 100644 --- a/Base/ASAssert.h +++ b/Base/ASAssert.h @@ -13,46 +13,48 @@ #import #import -#define ASDisplayNodeAssertWithSignalAndLogFunction(condition, description, logFunction, ...) NSAssert(condition, description, ##__VA_ARGS__); -#define ASDisplayNodeCAssertWithSignalAndLogFunction(condition, description, logFunction, ...) NSCAssert(condition, description, ##__VA_ARGS__); -#define ASDisplayNodeAssertWithSignal(condition, description, ...) NSAssert(condition, description, ##__VA_ARGS__) -#define ASDisplayNodeCAssertWithSignal(condition, description, ...) NSCAssert(condition, description, ##__VA_ARGS__) - #define ASDISPLAYNODE_ASSERTIONS_ENABLED (!defined(NS_BLOCK_ASSERTIONS)) -#define ASDisplayNodeAssert(...) NSAssert(__VA_ARGS__) -#define ASDisplayNodeCAssert(...) NSCAssert(__VA_ARGS__) +/** + * Note: In some cases it would be sufficient to do e.g.: + * ASDisplayNodeAssert(...) NSAssert(__VA_ARGS__) + * but we prefer not to, because we want to match the autocomplete behavior of NSAssert. + * The construction listed above does not show the user what arguments are required and what are optional. + */ -#define ASDisplayNodeAssertNil(condition, description, ...) ASDisplayNodeAssertWithSignal(!(condition), nil, (description), ##__VA_ARGS__) -#define ASDisplayNodeCAssertNil(condition, description, ...) ASDisplayNodeCAssertWithSignal(!(condition), nil, (description), ##__VA_ARGS__) +#define ASDisplayNodeAssert(condition, desc, ...) NSAssert(condition, desc, ##__VA_ARGS__) +#define ASDisplayNodeCAssert(condition, desc, ...) NSCAssert(condition, desc, ##__VA_ARGS__) -#define ASDisplayNodeAssertNotNil(condition, description, ...) ASDisplayNodeAssertWithSignal((condition), nil, (description), ##__VA_ARGS__) -#define ASDisplayNodeCAssertNotNil(condition, description, ...) ASDisplayNodeCAssertWithSignal((condition), nil, (description), ##__VA_ARGS__) +#define ASDisplayNodeAssertNil(condition, desc, ...) ASDisplayNodeAssert((condition) == nil, desc, ##__VA_ARGS__) +#define ASDisplayNodeCAssertNil(condition, desc, ...) ASDisplayNodeCAssert((condition) == nil, desc, ##__VA_ARGS__) -#define ASDisplayNodeAssertImplementedBySubclass() ASDisplayNodeAssertWithSignal(NO, nil, @"This method must be implemented by subclass %@", [self class]); -#define ASDisplayNodeAssertNotInstantiable() ASDisplayNodeAssertWithSignal(NO, nil, @"This class is not instantiable."); -#define ASDisplayNodeAssertNotSupported() ASDisplayNodeAssertWithSignal(NO, nil, @"This method is not supported by class %@", [self class]); +#define ASDisplayNodeAssertNotNil(condition, desc, ...) ASDisplayNodeAssert((condition) != nil, desc, ##__VA_ARGS__) +#define ASDisplayNodeCAssertNotNil(condition, desc, ...) ASDisplayNodeCAssert((condition) != nil, desc, ##__VA_ARGS__) -#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssertWithSignal(0 != pthread_main_np(), nil, @"This method must be called on the main thread") -#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssertWithSignal(0 != pthread_main_np(), nil, @"This function must be called on the main thread") +#define ASDisplayNodeAssertImplementedBySubclass() ASDisplayNodeAssert(NO, @"This method must be implemented by subclass %@", [self class]); +#define ASDisplayNodeAssertNotInstantiable() ASDisplayNodeAssert(NO, nil, @"This class is not instantiable."); +#define ASDisplayNodeAssertNotSupported() ASDisplayNodeAssert(NO, nil, @"This method is not supported by class %@", [self class]); -#define ASDisplayNodeAssertNotMainThread() ASDisplayNodeAssertWithSignal(0 == pthread_main_np(), nil, @"This method must be called off the main thread") -#define ASDisplayNodeCAssertNotMainThread() ASDisplayNodeCAssertWithSignal(0 == pthread_main_np(), nil, @"This function must be called off the main thread") +#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssert(0 != pthread_main_np(), @"This method must be called on the main thread") +#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssert(0 != pthread_main_np(), @"This function must be called on the main thread") -#define ASDisplayNodeAssertFlag(X) ASDisplayNodeAssertWithSignal((1 == __builtin_popcount(X)), nil, nil) -#define ASDisplayNodeCAssertFlag(X) ASDisplayNodeCAssertWithSignal((1 == __builtin_popcount(X)), nil, nil) +#define ASDisplayNodeAssertNotMainThread() ASDisplayNodeAssert(0 == pthread_main_np(), @"This method must be called off the main thread") +#define ASDisplayNodeCAssertNotMainThread() ASDisplayNodeCAssert(0 == pthread_main_np(), @"This function must be called off the main thread") -#define ASDisplayNodeAssertTrue(condition) ASDisplayNodeAssertWithSignal((condition), nil, nil) -#define ASDisplayNodeCAssertTrue(condition) ASDisplayNodeCAssertWithSignal((condition), nil, nil) +#define ASDisplayNodeAssertFlag(X, desc, ...) ASDisplayNodeAssert((1 == __builtin_popcount(X)), desc, ##__VA_ARGS__) +#define ASDisplayNodeCAssertFlag(X, desc, ...) ASDisplayNodeCAssert((1 == __builtin_popcount(X)), desc, ##__VA_ARGS__) -#define ASDisplayNodeAssertFalse(condition) ASDisplayNodeAssertWithSignal(!(condition), nil, nil) -#define ASDisplayNodeCAssertFalse(condition) ASDisplayNodeCAssertWithSignal(!(condition), nil, nil) +#define ASDisplayNodeAssertTrue(condition) ASDisplayNodeAssert((condition), @"Expected %s to be true.", #condition) +#define ASDisplayNodeCAssertTrue(condition) ASDisplayNodeCAssert((condition), @"Expected %s to be true.", #condition) -#define ASDisplayNodeFailAssert(description, ...) ASDisplayNodeAssertWithSignal(NO, (description), ##__VA_ARGS__) -#define ASDisplayNodeCFailAssert(description, ...) ASDisplayNodeCAssertWithSignal(NO, (description), ##__VA_ARGS__) +#define ASDisplayNodeAssertFalse(condition) ASDisplayNodeAssert(!(condition), @"Expected %s to be false.", #condition) +#define ASDisplayNodeCAssertFalse(condition) ASDisplayNodeCAssert(!(condition), @"Expected %s to be false.", #condition) -#define ASDisplayNodeConditionalAssert(shouldTestCondition, condition, description, ...) ASDisplayNodeAssert((!(shouldTestCondition) || (condition)), nil, (description), ##__VA_ARGS__) -#define ASDisplayNodeConditionalCAssert(shouldTestCondition, condition, description, ...) ASDisplayNodeCAssert((!(shouldTestCondition) || (condition)), nil, (description), ##__VA_ARGS__) +#define ASDisplayNodeFailAssert(desc, ...) ASDisplayNodeAssert(NO, desc, ##__VA_ARGS__) +#define ASDisplayNodeCFailAssert(desc, ...) ASDisplayNodeCAssert(NO, desc, ##__VA_ARGS__) + +#define ASDisplayNodeConditionalAssert(shouldTestCondition, condition, desc, ...) ASDisplayNodeAssert((!(shouldTestCondition) || (condition)), desc, ##__VA_ARGS__) +#define ASDisplayNodeConditionalCAssert(shouldTestCondition, condition, desc, ...) ASDisplayNodeCAssert((!(shouldTestCondition) || (condition)), desc, ##__VA_ARGS__) #define ASDisplayNodeCAssertPositiveReal(description, num) ASDisplayNodeCAssert(num >= 0 && num <= CGFLOAT_MAX, @"%@ must be a real positive integer.", description) #define ASDisplayNodeCAssertInfOrPositiveReal(description, num) ASDisplayNodeCAssert(isinf(num) || (num >= 0 && num <= CGFLOAT_MAX), @"%@ must be infinite or a real positive integer.", description) diff --git a/README.md b/README.md index 6631041e76..d1d04855b5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![AsyncDisplayKit](https://github.com/facebook/AsyncDisplayKit/blob/gh-pages/static/images/logo.png) +![AsyncDisplayKit](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/logo.png) [![Apps Using](https://img.shields.io/badge/Apps%20Using%20ASDK-%3E4,974-28B9FE.svg)](http://cocoapods.org/pods/AsyncDisplayKit) [![Downloads](https://img.shields.io/badge/Total%20Downloads-%3E512,000-28B9FE.svg)](http://cocoapods.org/pods/AsyncDisplayKit) @@ -10,10 +10,10 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-59C939.svg?style=flat)](https://github.com/Carthage/Carthage) [![Build Status](https://travis-ci.org/facebook/AsyncDisplayKit.svg)](https://travis-ci.org/facebook/AsyncDisplayKit) [![License](https://img.shields.io/cocoapods/l/AsyncDisplayKit.svg)](https://github.com/facebook/AsyncDisplayKit/blob/master/LICENSE) - + ## Installation -ASDK is available via CocoaPods or Carthage. See our [Installation](http://asyncdisplaykit.org/docs/installation.html) guide for instructions. +ASDK is available via CocoaPods or Carthage. See our [Installation](http://asyncdisplaykit.org/docs/installation.html) guide for instructions. ## Performance Gains @@ -21,7 +21,7 @@ AsyncDisplayKit's basic unit is the `node`. An ASDisplayNode is an abstraction o To keep its user interface smooth and responsive, your app should render at 60 frames per second — the gold standard on iOS. This means the main thread has one-sixtieth of a second to push each frame. That's 16 milliseconds to execute all layout and drawing code! And because of system overhead, your code usually has less than ten milliseconds to run before it causes a frame drop. -AsyncDisplayKit lets you move image decoding, text sizing and rendering, layout, and other expensive UI operations off the main thread, to keep the main thread available to respond to user interaction. +AsyncDisplayKit lets you move image decoding, text sizing and rendering, layout, and other expensive UI operations off the main thread, to keep the main thread available to respond to user interaction. ## Advanced Developer Features @@ -35,7 +35,7 @@ As the framework has grown, many features have been added that can save develope ## Getting Help -We use Slack for real-time debugging, community updates, and general talk about ASDK. [Signup](http://asdk-slack-auto-invite.herokuapp.com) youself or email AsyncDisplayKit(at)gmail.com to get an invite. +We use Slack for real-time debugging, community updates, and general talk about ASDK. [Signup](http://asdk-slack-auto-invite.herokuapp.com) yourself or email AsyncDisplayKit(at)gmail.com to get an invite. ## Contributing diff --git a/examples/ASDKgram/Sample/PhotoCellNode.m b/examples/ASDKgram/Sample/PhotoCellNode.m index 176cf4b608..cde6508c51 100644 --- a/examples/ASDKgram/Sample/PhotoCellNode.m +++ b/examples/ASDKgram/Sample/PhotoCellNode.m @@ -200,9 +200,9 @@ #pragma mark - Instance Methods -- (void)fetchData +- (void)didEnterPreloadState { - [super fetchData]; + [super didEnterPreloadState]; [_photoModel.commentFeed refreshFeedWithCompletionBlock:^(NSArray *newComments) { [self loadCommentsForPhoto:_photoModel];