在iOS的开发中,UITableView是最经常用到的UI组件之一。然而,UITableView却非常容易引起「NSInternalInconsistencyException(数据不一致)」的崩溃。其中,调用reloadSections时,一不留神就会引发崩溃。
reloadSections简介
当UITableView的数据源变化时,通常会调用reloadData
或者reloadSections:withRowAnimation:
通知UITableView刷新UI。单从UI角度考虑,两者最大的区别就是reloadData
没有动画。所以,一般为用户体验考虑,我一般使用reloadSections:withRowAnimation:
。
reloadSections引发崩溃
调用reloadSections:withRowAnimation:
方法时,UITableView会校验其他section,如果发现UITableView内记录的某section的row的数量和[dataSource tableView:numberOfRowsInSection]
返回的不一致时,抛出NSInternalInconsistencyException
异常。
崩溃案例
其实reloadSections引起崩溃的原因非常简单。但是虽然简单,还是很容易在不经意间引起崩溃。那么继续来看下具体的案例,加深下印象。
- 案例一:延迟reload场景。
出于业务的某些需要,当SectionOne的数据源个数变化时,延迟刷新TableView。
1 | - (void)onSectionOneUpdate |
那么在这0.1秒当中,对其他section进行reload则会引发崩溃。
1 | - (void)reloadSectionTwo |
崩溃的原因自然是因为SectionOne的数据源个数和UITableView中的不一致导致。要解决这个场景的崩溃其实也很简单。用一个NSMutableIndexSet
变量记录需要reload的section。
1 | - (void)onSectionOneUpdate |
- 案例二:Section的numberOfRow依赖于其他Section
UITableView有两个Section。整个UITableView都没有数据时,需要在section0中显示一个特殊的EmptyCell
,提示用户当前UITableView没有数据。那么先看下[dataSource tableView:numberOfRowsInSection:]
的实现。
1 | // dataSource |
那么当程序按照以下步骤运行时,就必然崩溃。
- UITableView没有数据。section0中有一个EmptyCell。
- secton1数据源增加一个item
- 调用reloadSections,只刷新section1。程序崩溃。
section1数据源增加item时,其实也影响到了section0。单纯刷新section1就会崩溃了。
对于这种场景,简单的做法是特别处理item个数由0增加至1的情况,调用reloadData进行刷新。但是我个人认为,EmptyCell不应该由这种方式来实现。使用UITableView时,需要保证数据源item和UITableViewCell一一对应。凭空捏造一个EmptyCell不好维护。容易导致NSInternalInconsistencyException
。