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;
|
- (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
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
NSMutableArray *_batchUpdateBlocks;
|
NSMutableArray *_batchUpdateBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ASCollectionView
|
@implementation ASCollectionView
|
||||||
@@ -131,6 +133,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
||||||
|
|
||||||
|
_asyncDataSourceLocked = NO;
|
||||||
|
|
||||||
_performingBatchUpdates = NO;
|
_performingBatchUpdates = NO;
|
||||||
_batchUpdateBlocks = [NSMutableArray array];
|
_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 -
|
||||||
#pragma mark ASRangeControllerDelegate.
|
#pragma mark ASRangeControllerDelegate.
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,26 @@
|
|||||||
*/
|
*/
|
||||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath;
|
- (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
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
ASRangeController *_rangeController;
|
ASRangeController *_rangeController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property (atomic, assign) BOOL asyncDataSouceLocked;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ASTableView
|
@implementation ASTableView
|
||||||
@@ -137,6 +139,8 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||||
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
||||||
|
|
||||||
|
_asyncDataSouceLocked = NO;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,6 +429,28 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
return CGSizeMake(self.bounds.size.width, FLT_MAX);
|
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
|
- (NSUInteger)dataController:(ASDataController *)dataControllre rowsInSection:(NSUInteger)section
|
||||||
{
|
{
|
||||||
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
|
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
|
||||||
|
|||||||
@@ -3,6 +3,19 @@
|
|||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import <AsyncDisplayKit/ASDealloc2MainObject.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 ASCellNode;
|
||||||
@class ASDataController;
|
@class ASDataController;
|
||||||
|
|
||||||
@@ -27,13 +40,23 @@ typedef NSUInteger ASDataControllerAnimationOptions;
|
|||||||
/**
|
/**
|
||||||
Fetch the number of rows in specific section.
|
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.
|
Fetch the number of sections.
|
||||||
*/
|
*/
|
||||||
- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController;
|
- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Lock the data source for data fetching.
|
||||||
|
*/
|
||||||
|
- (void)dataControllerLockDataSourceForDataUpdating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Unlock the data source after data fetching.
|
||||||
|
*/
|
||||||
|
- (void)dataControllerUnlockDataSourceForDataUpdating;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,6 +10,18 @@
|
|||||||
#import "ASMultidimensionalArrayUtils.h"
|
#import "ASMultidimensionalArrayUtils.h"
|
||||||
#import "ASDisplayNodeInternal.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) \
|
#define INSERT_NODES(multidimensionalArray, indexPath, elements, animationOption) \
|
||||||
{ \
|
{ \
|
||||||
if ([_delegate respondsToSelector:@selector(dataController:willInsertNodes:atIndexPaths:withAnimationOption:)]) { \
|
if ([_delegate respondsToSelector:@selector(dataController:willInsertNodes:atIndexPaths:withAnimationOption:)]) { \
|
||||||
@@ -131,7 +143,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
#pragma mark - Initial Data Loading
|
#pragma mark - Initial Data Loading
|
||||||
|
|
||||||
- (void)initialDataLoadingWithAnimationOption:(ASDataControllerAnimationOptions)animationOption {
|
- (void)initialDataLoadingWithAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
|
{
|
||||||
|
BEGIN_DATA_FETCHING
|
||||||
|
|
||||||
NSMutableArray *indexPaths = [NSMutableArray array];
|
NSMutableArray *indexPaths = [NSMutableArray array];
|
||||||
|
|
||||||
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
|
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
|
||||||
@@ -150,6 +165,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
// insert elements
|
// insert elements
|
||||||
[self insertRowsAtIndexPaths:indexPaths withAnimationOption:animationOption];
|
[self insertRowsAtIndexPaths:indexPaths withAnimationOption:animationOption];
|
||||||
|
|
||||||
|
END_DATA_FETCHING
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Data Update
|
#pragma mark - Data Update
|
||||||
@@ -187,6 +204,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
- (void)insertSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
- (void)insertSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
{
|
{
|
||||||
|
BEGIN_DATA_FETCHING
|
||||||
|
|
||||||
__block int nodeTotalCnt = 0;
|
__block int nodeTotalCnt = 0;
|
||||||
NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count];
|
NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count];
|
||||||
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||||
@@ -222,6 +241,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
END_DATA_FETCHING
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
@@ -239,6 +260,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
- (void)reloadSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
- (void)reloadSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
{
|
{
|
||||||
|
BEGIN_DATA_FETCHING
|
||||||
|
|
||||||
// We need to keep data query on data source in the calling thread.
|
// We need to keep data query on data source in the calling thread.
|
||||||
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
||||||
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
||||||
@@ -264,6 +287,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
// reinsert the elements
|
// reinsert the elements
|
||||||
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
|
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
END_DATA_FETCHING
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
- (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
|
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
{
|
{
|
||||||
|
BEGIN_DATA_FETCHING
|
||||||
|
|
||||||
// sort indexPath to avoid messing up the index when inserting in several batches
|
// sort indexPath to avoid messing up the index when inserting in several batches
|
||||||
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
||||||
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||||
@@ -357,6 +384,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
||||||
|
|
||||||
|
END_DATA_FETCHING
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
@@ -373,6 +402,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
{
|
{
|
||||||
|
BEGIN_DATA_FETCHING
|
||||||
|
|
||||||
// The reloading operation required reloading the data
|
// The reloading operation required reloading the data
|
||||||
// Loading data in the calling thread
|
// Loading data in the calling thread
|
||||||
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||||
@@ -388,6 +419,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
END_DATA_FETCHING
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
@@ -407,6 +440,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
- (void)reloadDataWithAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
- (void)reloadDataWithAnimationOption:(ASDataControllerAnimationOptions)animationOption
|
||||||
{
|
{
|
||||||
|
BEGIN_DATA_FETCHING
|
||||||
|
|
||||||
// Fetching data in calling thread
|
// Fetching data in calling thread
|
||||||
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
||||||
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
||||||
@@ -446,6 +481,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
|
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
END_DATA_FETCHING
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Data Querying
|
#pragma mark - Data Querying
|
||||||
|
|||||||
Reference in New Issue
Block a user