diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 0f18515d2e..98863648c3 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -144,12 +144,11 @@ 292C59A21A956527007E5DD6 /* ASRangeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599C1A956527007E5DD6 /* ASRangeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 292C59A31A956527007E5DD6 /* ASRangeHandlerRender.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */; settings = {ATTRIBUTES = (Public, ); }; }; 292C59A41A956527007E5DD6 /* ASRangeHandlerRender.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */; }; - 299DA1A91A828D2900162D41 /* ASBatchContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 299DA1A71A828D2900162D41 /* ASBatchContext.h */; }; - 296A0A2E1A9516B2005ACEAA /* ASBatchFetching.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 296A0A2E1A9516B2005ACEAA /* ASBatchFetching.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */; settings = {ATTRIBUTES = (Private, ); }; }; 296A0A2F1A9516B2005ACEAA /* ASBatchFetching.m in Sources */ = {isa = PBXBuildFile; fileRef = 296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */; }; 296A0A351A951ABF005ACEAA /* ASBatchFetchingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */; }; 299DA1A91A828D2900162D41 /* ASBatchContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 299DA1A71A828D2900162D41 /* ASBatchContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 299DA1AA1A828D2900162D41 /* ASBatchContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 299DA1A81A828D2900162D41 /* ASBatchContext.m */; }; + 299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 299DA1A81A828D2900162D41 /* ASBatchContext.mm */; }; 3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 464052201A3F83C40061C0BA /* ASDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 464052191A3F83C40061C0BA /* ASDataController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 464052211A3F83C40061C0BA /* ASDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521A1A3F83C40061C0BA /* ASDataController.mm */; }; @@ -302,12 +301,12 @@ 292C599C1A956527007E5DD6 /* ASRangeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandler.h; sourceTree = ""; }; 292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerRender.h; sourceTree = ""; }; 292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRangeHandlerRender.mm; sourceTree = ""; }; - 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBatchFetching.h; sourceTree = ""; }; - 296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBatchFetching.m; sourceTree = ""; }; + 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBatchFetching.h; path = ../Details/ASBatchFetching.h; sourceTree = ""; }; + 296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASBatchFetching.m; path = ../Details/ASBatchFetching.m; sourceTree = ""; }; 296A0A311A951715005ACEAA /* ASScrollDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASScrollDirection.h; path = AsyncDisplayKit/Details/ASScrollDirection.h; sourceTree = SOURCE_ROOT; }; 296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBatchFetchingTests.m; sourceTree = ""; }; 299DA1A71A828D2900162D41 /* ASBatchContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBatchContext.h; sourceTree = ""; }; - 299DA1A81A828D2900162D41 /* ASBatchContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBatchContext.m; sourceTree = ""; }; + 299DA1A81A828D2900162D41 /* ASBatchContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBatchContext.mm; sourceTree = ""; }; 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableViewTests.m; sourceTree = ""; }; 464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDataController.h; sourceTree = ""; }; 4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDataController.mm; sourceTree = ""; }; @@ -479,9 +478,7 @@ 054963471A1EA066000F8E56 /* ASBasicImageDownloader.h */, 054963481A1EA066000F8E56 /* ASBasicImageDownloader.mm */, 299DA1A71A828D2900162D41 /* ASBatchContext.h */, - 299DA1A81A828D2900162D41 /* ASBatchContext.m */, - 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */, - 296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */, + 299DA1A81A828D2900162D41 /* ASBatchContext.mm */, 464052191A3F83C40061C0BA /* ASDataController.h */, 4640521A1A3F83C40061C0BA /* ASDataController.mm */, 05A6D05819D0EB64002DD95E /* ASDealloc2MainObject.h */, @@ -546,6 +543,8 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */, + 296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */, 058D0A02195D050800B7D73C /* _AS-objc-internal.h */, 058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */, 058D0A04195D050800B7D73C /* _ASCoreAnimationExtras.mm */, @@ -809,7 +808,7 @@ 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, 464052211A3F83C40061C0BA /* ASDataController.mm in Sources */, - 299DA1AA1A828D2900162D41 /* ASBatchContext.m in Sources */, + 299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */, 058D0A15195D050800B7D73C /* ASDisplayNodeExtras.mm in Sources */, 058D0A1F195D050800B7D73C /* ASTextNodeTextKitHelpers.mm in Sources */, 055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */, @@ -862,6 +861,7 @@ 058D0A3B195D057000B7D73C /* ASDisplayNodeTestsHelper.m in Sources */, 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.m in Sources */, 052EE0661A159FEF002C6279 /* ASMultiplexImageNodeTests.m in Sources */, + 291D92411A9D537B008286B8 /* ASBatchFetching.m in Sources */, 058D0A39195D057000B7D73C /* ASDisplayNodeAppearanceTests.m in Sources */, 058D0A41195D057000B7D73C /* ASTextNodeWordKernerTests.mm in Sources */, 058D0A40195D057000B7D73C /* ASTextNodeTests.m in Sources */, diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index da640c61b9..fb28fa5eb3 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -174,6 +174,21 @@ - (void)collectionView:(ASCollectionView *)collectionView willDisplayNodeForItemAtIndexPath:(NSIndexPath *)indexPath; - (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNodeForItemAtIndexPath:(NSIndexPath*)indexPath; +/** + * Receive a message that the collectionView is near the end of its data set and more data should be fetched if + * necessary. + * + * @param tableView The sender. + * @param context A context object that must be notified when the batch fetch is completed. + * + * @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future + * notifications to do batch fetches. This method is called on a background queue. + * + * UICollectionView currently only supports batch events for tail loads. If you require a head load, consider + * implementing a UIRefreshControl. + */ +- (void)collectionView:(ASCollectionView *)collectionView willBeginBatchFetchWithContext:(ASBatchContext *)context; + /** * Tell the collectionView if batch fetching should begin. * @@ -187,21 +202,6 @@ */ - (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView; -/** - * Receive a message that the collectionView is near the end of its data set and more data should be fetched if - * necessary. - * - * @param tableView The sender. - * @param context A context object that must be notified when the batch fetch is completed. - * - * @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future - * notifications to do batch fetches. - * - * UICollectionView currently only supports batch events for tail loads. If you require a head load, consider - * implementing a UIRefreshControl. - */ -- (void)collectionView:(ASCollectionView *)collectionView beginBatchFetchingWithContext:(ASBatchContext *)context; - @end @interface ASCollectionView (Deprecated) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 60ac8b1a7b..4463ae6090 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -384,13 +384,13 @@ static BOOL _isInterceptedSelector(SEL sel) } } -- (BOOL)shouldFetchBatch +- (BOOL)shouldBatchFetch { - if ([self.asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)]) { - return [self.asyncDelegate shouldBatchFetchForCollectionView:self]; + if ([_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)]) { + return [_asyncDelegate shouldBatchFetchForCollectionView:self]; } else { // if the delegate does not respond to this method, there is no point in starting to fetch - return [self.asyncDelegate respondsToSelector:@selector(collectionView:beginBatchFetchingWithContext:)]; + return [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)]; } } @@ -398,13 +398,15 @@ static BOOL _isInterceptedSelector(SEL sel) { ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist"); - if (![self shouldFetchBatch]) { + if (![self shouldBatchFetch]) { return; } if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) { [_batchContext beginBatchFetching]; - [self.asyncDelegate collectionView:self beginBatchFetchingWithContext:_batchContext]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [_asyncDelegate collectionView:self willBeginBatchFetchWithContext:_batchContext]; + }); } } diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 08ac6be7a9..930afbd80c 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -179,19 +179,6 @@ - (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath; - (void)tableView:(ASTableView *)tableView didEndDisplayingNodeForRowAtIndexPath:(NSIndexPath*)indexPath; -/** - * Tell the tableView if batch fetching should begin. - * - * @param tableView The sender. - * - * @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of - * objects that can be fetched or no network connection. - * - * If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching - * should occur. - */ -- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView; - /** * Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary. * @@ -199,12 +186,25 @@ * @param context A context object that must be notified when the batch fetch is completed. * * @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future - * notifications to do batch fetches. + * notifications to do batch fetches. This method is called on a background queue. * * ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a * UIRefreshControl. */ -- (void)tableView:(ASTableView *)tableView beginBatchFetchingWithContext:(ASBatchContext *)context; +- (void)tableView:(ASTableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context; + +/** + * Tell the tableView if batch fetching should begin. + * + * @param tableView The sender. + * + * @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of + * objects that can be fetched or no network connection. + * + * If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching + * should occur. + */ +- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView; @end diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index a1fbfa7550..6ccf51fd77 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -390,13 +390,13 @@ static BOOL _isInterceptedSelector(SEL sel) } } -- (BOOL)shouldFetchBatch +- (BOOL)shouldBatchFetch { - if ([self.asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)]) { - return [self.asyncDelegate shouldBatchFetchForTableView:self]; + if ([_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)]) { + return [_asyncDelegate shouldBatchFetchForTableView:self]; } else { // if the delegate does not respond to this method, there is no point in starting to fetch - return [self.asyncDelegate respondsToSelector:@selector(tableView:beginBatchFetchingWithContext:)]; + return [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)]; } } @@ -404,13 +404,15 @@ static BOOL _isInterceptedSelector(SEL sel) { ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist"); - if (![self shouldFetchBatch]) { + if (![self shouldBatchFetch]) { return; } if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) { [_batchContext beginBatchFetching]; - [self.asyncDelegate tableView:self beginBatchFetchingWithContext:_batchContext]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [_asyncDelegate tableView:self willBeginBatchFetchWithContext:_batchContext]; + }); } } diff --git a/AsyncDisplayKit/Details/ASBatchContext.h b/AsyncDisplayKit/Details/ASBatchContext.h index 2d8f2bb2e1..94c42293b2 100644 --- a/AsyncDisplayKit/Details/ASBatchContext.h +++ b/AsyncDisplayKit/Details/ASBatchContext.h @@ -53,7 +53,7 @@ * Notify the context object that fetching has started. * * @discussion Call this method only when you are beginning a fetch process. This should really only be called by the - * context object's owner. Calling this method should be complimented with -completeBatchFetching:. + * context object's owner. Calling this method should be paired with -completeBatchFetching:. */ - (void)beginBatchFetching; diff --git a/AsyncDisplayKit/Details/ASBatchContext.m b/AsyncDisplayKit/Details/ASBatchContext.mm similarity index 81% rename from AsyncDisplayKit/Details/ASBatchContext.m rename to AsyncDisplayKit/Details/ASBatchContext.mm index f3cbb0f7b9..4833cbd826 100644 --- a/AsyncDisplayKit/Details/ASBatchContext.m +++ b/AsyncDisplayKit/Details/ASBatchContext.mm @@ -8,6 +8,8 @@ #import "ASBatchContext.h" +#import "ASThread.h" + typedef NS_ENUM(NSInteger, ASBatchContextState) { ASBatchContextStateFetching, ASBatchContextStateCancelled, @@ -17,6 +19,7 @@ typedef NS_ENUM(NSInteger, ASBatchContextState) { @interface ASBatchContext () { ASBatchContextState _state; + ASDN::RecursiveMutex _propertyLock; } @end @@ -32,28 +35,33 @@ typedef NS_ENUM(NSInteger, ASBatchContextState) { - (BOOL)isFetching { + ASDN::MutexLocker l(_propertyLock); return _state == ASBatchContextStateFetching; } - (BOOL)batchFetchingWasCancelled { + ASDN::MutexLocker l(_propertyLock); return _state == ASBatchContextStateCancelled; } - (void)completeBatchFetching:(BOOL)didComplete { if (didComplete) { + ASDN::MutexLocker l(_propertyLock); _state = ASBatchContextStateCompleted; } } - (void)beginBatchFetching { + ASDN::MutexLocker l(_propertyLock); _state = ASBatchContextStateFetching; } - (void)cancelBatchFetching { + ASDN::MutexLocker l(_propertyLock); _state = ASBatchContextStateCancelled; }