App 测试时 一次偶然的 UITableView 崩溃调试过程

最近在做一款医疗类的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).’

崩溃主线程函数帧栈:

thread

多次操作复现问题:

点击不喜欢按钮,然后刷新医生列表,点击喜欢按钮,然后刷新列表。发现并不是每次都会出现崩溃,有时出现,有时又不会出现。

分析问题:

从上面的信息,第一时间想到的就是: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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 社会阅历浅的人,当冒犯发生时,会第一时间追随原始情绪进行反击,然后这样的反击多半没有深入思考,并缺乏策略,最终的结...
    天空的羁绊阅读 3,875评论 0 0
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 33,537评论 18 399
  • 多才多艺
    叶囡阅读 1,562评论 0 0

友情链接更多精彩内容