mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Move ASTableView & ASCollectionView data fetching to background thread
Conflicts: AsyncDisplayKit/ASCollectionView.mm AsyncDisplayKit/Details/ASDataController.mm Conflicts: AsyncDisplayKit/ASCollectionView.mm AsyncDisplayKit/Details/ASDataController.mm
This commit is contained in:
@@ -107,6 +107,26 @@
|
||||
*/
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Indicator to lock the data source for data loading in asyn mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*/
|
||||
- (void)collectionViewLockDataSourceForDataUpdating:(ASCollectionView *)collectionView;
|
||||
|
||||
/**
|
||||
* Indicator to unlock the data source for data loading in asyn mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*/
|
||||
- (void)collectionViewUnlockDataSourceForDataUpdating:(ASCollectionView *)collectionView;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -103,6 +103,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
NSMutableArray *_batchUpdateBlocks;
|
||||
}
|
||||
|
||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASCollectionView
|
||||
@@ -131,6 +133,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
||||
|
||||
_asyncDataSourceLocked = NO;
|
||||
|
||||
_performingBatchUpdates = NO;
|
||||
_batchUpdateBlocks = [NSMutableArray array];
|
||||
|
||||
@@ -375,6 +379,26 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dataControllerLockDataSourceForDataUpdating
|
||||
{
|
||||
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked");
|
||||
|
||||
self.asyncDataSourceLocked = YES;
|
||||
if ([_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSourceForDataUpdating:)]) {
|
||||
[_asyncDataSource collectionViewLockDataSourceForDataUpdating:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dataControllerUnlockDataSourceForDataUpdating
|
||||
{
|
||||
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has alredy been unlocked !");
|
||||
|
||||
self.asyncDataSourceLocked = NO;
|
||||
if ([_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSourceForDataUpdating:)]) {
|
||||
[_asyncDataSource collectionViewUnlockDataSourceForDataUpdating:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASRangeControllerDelegate.
|
||||
|
||||
|
||||
@@ -109,6 +109,26 @@
|
||||
*/
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Indicator to lock the data source for data loading in asyn mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*/
|
||||
- (void)tableViewLockDataSourceForDataUpdating:(ASTableView *)tableView;
|
||||
|
||||
/**
|
||||
* Indicator to unlock the data source for data loading in asyn mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*/
|
||||
- (void)tableViewUnlockDataSourceForDataUpdating:(ASTableView *)tableView;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -112,6 +112,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
ASRangeController *_rangeController;
|
||||
}
|
||||
|
||||
@property (atomic, assign) BOOL asyncDataSouceLocked;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASTableView
|
||||
@@ -137,6 +139,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
||||
|
||||
_asyncDataSouceLocked = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -425,6 +429,28 @@ static BOOL _isInterceptedSelector(SEL sel)
|
||||
return CGSizeMake(self.bounds.size.width, FLT_MAX);
|
||||
}
|
||||
|
||||
- (void)dataControllerLockDataSourceForDataUpdating
|
||||
{
|
||||
ASDisplayNodeAssert(!self.asyncDataSouceLocked, @"The data source has already been locked !");
|
||||
|
||||
self.asyncDataSouceLocked = YES;
|
||||
|
||||
if ([_asyncDataSource respondsToSelector:@selector(tableViewLockDataSourceForDataUpdating:)]) {
|
||||
[_asyncDataSource tableViewLockDataSourceForDataUpdating:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dataControllerUnlockDataSourceForDataUpdating
|
||||
{
|
||||
ASDisplayNodeAssert(self.asyncDataSouceLocked, @"The data source has already been unlocked !");
|
||||
|
||||
self.asyncDataSouceLocked = NO;
|
||||
|
||||
if ([_asyncDataSource respondsToSelector:@selector(tableViewUnlockDataSourceForDataUpdating:)]) {
|
||||
[_asyncDataSource tableViewUnlockDataSourceForDataUpdating:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)dataController:(ASDataController *)dataControllre rowsInSection:(NSUInteger)section
|
||||
{
|
||||
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AsyncDisplayKit/ASDealloc2MainObject.h>
|
||||
|
||||
/**
|
||||
Enable the data fetching in async mode.
|
||||
|
||||
If enabled, we will fetch data through `dataController:nodeAtIndexPath:` and `dataController:rowsInSection:` in background thread.
|
||||
Otherwise, the methods will be invoked synchronically in calling thread.
|
||||
|
||||
Enabling data fetching in async mode could avoid blocking main thread while allocating cell on main thread, which is frequently reported
|
||||
issue for handing large scale data. On another hand, the application code will take the responsibility to avoid data inconsistence.
|
||||
Specifically, we will lock the data source through `dataControllerLockDataSourceForDataUpdating`, and unlock it by `dataControllerUnlockDataSourceForDataUpdating`
|
||||
after the data fetching. The application should not update the data source while the data source is locked.
|
||||
*/
|
||||
#define ENABLE_ASYNC_DATA_FETCHING 1
|
||||
|
||||
@class ASCellNode;
|
||||
@class ASDataController;
|
||||
|
||||
@@ -27,13 +40,23 @@ typedef NSUInteger ASDataControllerAnimationOptions;
|
||||
/**
|
||||
Fetch the number of rows in specific section.
|
||||
*/
|
||||
- (NSUInteger)dataController:(ASDataController *)dataControllre rowsInSection:(NSUInteger)section;
|
||||
- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section;
|
||||
|
||||
/**
|
||||
Fetch the number of sections.
|
||||
*/
|
||||
- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController;
|
||||
|
||||
/**
|
||||
Lock the data source for data fetching.
|
||||
*/
|
||||
- (void)dataControllerLockDataSourceForDataUpdating;
|
||||
|
||||
/**
|
||||
Unlock the data source after data fetching.
|
||||
*/
|
||||
- (void)dataControllerUnlockDataSourceForDataUpdating;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,18 @@
|
||||
#import "ASMultidimensionalArrayUtils.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
|
||||
#ifdef ENABLE_ASYNC_DATA_FETCHING
|
||||
#define BEGIN_DATA_FETCHING \
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ \
|
||||
[_dataSource dataControllerLockDataSourceForDataUpdating];
|
||||
#define END_DATA_FETCHING \
|
||||
[_dataSource dataControllerUnlockDataSourceForDataUpdating]; \
|
||||
});
|
||||
#else
|
||||
#define BEGIN_DATA_FETCHING
|
||||
#define END_DATA_FETCHING
|
||||
#endif
|
||||
|
||||
#define INSERT_NODES(multidimensionalArray, indexPath, elements, animationOption) \
|
||||
{ \
|
||||
if ([_delegate respondsToSelector:@selector(dataController:willInsertNodes:atIndexPaths:withAnimationOption:)]) { \
|
||||
@@ -131,7 +143,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
#pragma mark - Initial Data Loading
|
||||
|
||||
- (void)initialDataLoadingWithAnimationOption:(ASDataControllerAnimationOptions)animationOption {
|
||||
- (void)initialDataLoadingWithAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
{
|
||||
BEGIN_DATA_FETCHING
|
||||
|
||||
NSMutableArray *indexPaths = [NSMutableArray array];
|
||||
|
||||
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
|
||||
@@ -150,6 +165,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
// insert elements
|
||||
[self insertRowsAtIndexPaths:indexPaths withAnimationOption:animationOption];
|
||||
|
||||
END_DATA_FETCHING
|
||||
}
|
||||
|
||||
#pragma mark - Data Update
|
||||
@@ -187,6 +204,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
- (void)insertSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
{
|
||||
BEGIN_DATA_FETCHING
|
||||
|
||||
__block int nodeTotalCnt = 0;
|
||||
NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count];
|
||||
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||
@@ -222,6 +241,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
||||
});
|
||||
|
||||
END_DATA_FETCHING
|
||||
}
|
||||
|
||||
- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
@@ -239,6 +260,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
{
|
||||
BEGIN_DATA_FETCHING
|
||||
|
||||
// We need to keep data query on data source in the calling thread.
|
||||
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
||||
@@ -264,6 +287,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
// reinsert the elements
|
||||
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
|
||||
});
|
||||
|
||||
END_DATA_FETCHING
|
||||
}
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
@@ -349,6 +374,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
{
|
||||
BEGIN_DATA_FETCHING
|
||||
|
||||
// sort indexPath to avoid messing up the index when inserting in several batches
|
||||
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
||||
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||
@@ -357,6 +384,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
}
|
||||
|
||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
||||
|
||||
END_DATA_FETCHING
|
||||
}
|
||||
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
@@ -373,6 +402,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
{
|
||||
BEGIN_DATA_FETCHING
|
||||
|
||||
// The reloading operation required reloading the data
|
||||
// Loading data in the calling thread
|
||||
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||
@@ -388,6 +419,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
||||
});
|
||||
|
||||
END_DATA_FETCHING
|
||||
}
|
||||
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
@@ -407,6 +440,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
- (void)reloadDataWithAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||
{
|
||||
BEGIN_DATA_FETCHING
|
||||
|
||||
// Fetching data in calling thread
|
||||
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
||||
@@ -446,6 +481,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
|
||||
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
|
||||
});
|
||||
|
||||
END_DATA_FETCHING
|
||||
}
|
||||
|
||||
#pragma mark - Data Querying
|
||||
|
||||
Reference in New Issue
Block a user