最近在做一款医疗类的app,有一个 feed 流页面(医生列表)涉及到数据源删除插入操作。
相关操作:用户如果点击了不喜欢某个医生的按钮,就会把这个医生从他的医生列表删除。
调试信息:
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3694.4.18/UITableView.m:1950
* Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (19) must be equal to the number of rows contained in that section before the update (21), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).’
崩溃主线程函数帧栈:
多次操作复现问题:
点击不喜欢按钮,然后刷新医生列表,点击喜欢按钮,然后刷新列表。发现并不是每次都会出现崩溃,有时出现,有时又不会出现。
分析问题:
从上面的信息,第一时间想到的就是:table view 中显示的数据和其数据源不一致导致的。
删除医生列表时只使用了下面两句代码:
[self.listModel.dataList removeObjectAtIndex:indexPath.row];
[self.listView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
先删除数据源中的某一条数据,再使用 -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] 将数据源同步到 table view 上。按理说这样的操作并不会导致任何问题,但为什么这里执行到 -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] 时会崩溃呢?
我第一反应觉得应该多线程异步操作导致的问题,认为是在执行删除操作时其它地方在非主线程也对该列表进行了写操作。于是在删除数据源中的数据这行代码前后加了打印信息:
NSLog(@"count0: %ld", self.listModel.dataList.count);
NSLog(@"count1: %ld", [self.listView.dataSource tableView:self.listView numberOfRowsInSection:0]);
[self.listModel.dataList removeObjectAtIndex:indexPath.row];
NSLog(@"count2: %ld", self.listModel.dataList.count);
NSLog(@"count3: %ld", [self.listView.dataSource tableView:self.listView numberOfRowsInSection:0]);
打印信息:
count0: 23
count1: 23
count2: 22
count3: 22
这样看起来也没问题,也就是说很可能不是多线程引起的问题。
解决问题:
我就想到了很可能是有其它地方更改了数据源,但是并没有及时更新UI 界面导致的。
经过查找,确实发现有一行代码:
[self.listModel.dataList removeObject:object];
但并没有通过 reload/delete/insert 等方法对 table view 进行更新,导致 -[UITableView numberOfRowsInSection:] 方法返回的数目还是对数据源进行删除操作之前的数目。
因此只要在该行代码之后利用 reload/delete/insert 等方法对 table view 进行更新后,下一次在其他地方执行 reload/delete/insert 操作后也就不再崩溃了。
参考链接:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/TableView_iPhone/ManageInsertDeleteRow/ManageInsertDeleteRow.html#//apple_ref/doc/uid/TP40007451-CH10-SW1