UITableViewDataSource异步加载
我有一个UITableView
,这应该使条目分为部分。 数据需要处理一定的时间,因此它的工作是这样的:UITableViewDataSource异步加载
@interface AsyncTable()
NSMutableArray* _alphaKeys;
@end
@implementation AsyncTable
-(void)refreshData {
dispatch_async(_serialQueue, ^{
<load of the _alphaKeys>
dispatch_async(dispatch_get_main_queue(), ^{
[table reloadData];
});
});
}
...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [_alphaKeys count];
}
...
@end
现在,这个完美的作品首次。
但我的数据有时会改变,我从通知调用方法refreshData。 并且它正在开始崩溃,例如在执行numberOfSectionsInTableView
,并在cellForRowAtIndexPath
时间的阵列可以具有不同的内容的引用。
问题是时机,让我们假设有2级呼叫的每相继到来短: 第一个是加载完成并在主线程调度reloadData,然后第二个请求是与数据的重载启动主线程调用之前...
任何建议如何解决这个问题?我不想把所有东西都放在主线程中(它可以工作,我现在试图重构应用程序以使它更具响应性)
您的问题是由于更新数据源(_alphaKeys
)与tableView loading 。 UITableView异步加载,无法分辨何时完成。但是,如果更改数据源并立即调用reloadData,即使正在进行加载,它也会做正确的事情。诀窍是数据的改变必须在主线程上完成。
我对您的回答是更改您的代码,以便后台线程在一份数据上进行处理。当处理完成后,主线程新的数据复制到表视图的数据源,然后立即打电话reloadData
上运行的模块。
见我修改您的示例代码:
@interface AsyncTable()
NSArray* _alphaKeys; // <-- copy for data source is immutable
@end
@implementation AsyncTable
-(void)refreshData {
dispatch_async(_serialQueue, ^{
NSMutableArray *alphaKeysCopy = [_alphaKeys mutableCopy]; // <-- Create a new array if processing does not require current state
<load of the alphaKeysCopy>
dispatch_async(dispatch_get_main_queue(), ^{
_alphaKeys = [alphaKeysCopy copy]; // <-- Copy on the main thread so the table view can't see an intermediate state
[table reloadData];
});
});
}
谢谢!这是个好主意。另外一个问题是:'但是,如果更改数据源并立即调用reloadData,即使加载正在进行,它也会做正确的事情 - 您从哪里得到它?上面的@Mozilla提示起作用,但我不是100%肯定的,就像我不知道如何实现'reloadData'一样。如果这只是一堆异步调用来获取行数,在不同的块中绘制每一个raw等等,我的代码的第二次调用会在主要队列中的这些调用之间插入另一个_alphaKeys副本。你不这么认为? –
表视图(重新)加载是一个实现细节,但我的实验显示'reloadData'设置状态并触发该过程。在连续的循环中,调用数据源方法(numberOfRowsInSection,numberOfSections,cellForRowAtIndexPath等)来构建表。如果您在第一次调用之后在runloop迭代中调用'reloadData',则该表只会重置并重新开始。 – Avi
'reloadData' SUD没有在后台被调用,它的错误的方式重新加载'tableView' – Mukesh
@muku'reloadData'不是从后台调用,这是调用主线程(请参阅上面的代码)。或者我错过了这一点?我想在后台处理完成后调用reload –
嗨。 1)第一种方法是用'NSOperation'重构代码。这是允许您取消以前的操作。 2)第二种方法是添加一个追加加载状态的变量。在调用'[table reloadData]'之前,你需要检查这个变量。 –