相信不少人會遇到這種狀況,當tableView正在滾動的時候,若是reloadData,偶爾發生App crash的狀況。 這種狀況有時候有,有時候沒有,已經難倒了不少人。直至今天,我在stackoverflow上面,仍沒有發現真正有說到其本質的帖子。個人處女貼,選擇 這個問題來闡述一下個人觀點。
小弟我英語很好,通常都是用英語記筆記,固然,我知道,論壇憤青不少,若是隻貼英文出來,確定找罵。 故簡單翻譯一下,以顯示個人誠意。 原英文筆記附在後面。 請你們不要挑英語語法錯誤了,筆記就是筆記,不是出書。
第 一句話,闡述問題的本質:在tableView的dataSource被改變 和 tableView的reloadData被調用之間有個時間差,而正是在這個期間,tableView的delegate方法被調用,若是新的 dataSource的count小於原來的dataSource count,crash就頗有可能發生了。
下面的筆記提供了兩種解決方案,和記錄了一個典型的錯誤,即 在background thread
中修改了datasource,雖然調用
[
self
.
tableView
performSelectorOnMainThread:@selector(reloadData) withObject:nilwaitUntilDone:NO];
記住正確的原則: Always change the dataSource and(注意這個and) reloadData in the mainThread. What's more, reloadData should be called immediately after the dataSource change.
If dataSource is changed but tableView's reloadData method is not called immediately, the tableView may crash if it's in scrolling.
Crash Reason: There is still a time gap between the dataSource change and reloadData. If the table is scrolling during the time gap, the app may Crash!!!!
WRONG WAY:
Following codes is WRONG: even the reloadData is called in main thread, there is still a time gap between the dataSource change and reloadData. If the table is scrolling during the time gap, the app may Crash!!!!
wrong codes samples:
-(
void) changeDatasource_backgroundThread
{
@autoreleasepool
{
[
self
.
dataSourceArray
removeAllObjects];
[
self
.
tableView
performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
}
}
RIGHT WAY:
Principle: Always change dataSource in MAIN thread and call the reloadData immediately after it.
Option 1: If the operation to change the dataSource should be executed in background, the operation can create a temp dataSource array and pass it to main thread with notification, the main thread observes the notification, assign the tmpDataSource to dataSource and reload the tableView by reloadData.
Option 2: In the background, call the GDC dispatch_async to send the two methods to main thread together.
dispatch_async
(dispatch_get_main_queue
(), ^{
self.dataSourceArray= a new Array.
[self.tableView reloadData];
});