1.友盟错误信息

Invalid update: invalid number of rows in section .  The number of rows contained in an existing section after the update ()
must be equal to the number of rows contained in that section before the update (), plus or minus the number of rows inserted or deleted
from that section ( inserted, deleted) and plus or minus the number of rows moved into or out of that section ( moved in, moved out).
(null)

2.错误信息解析

上面的错误信息,大意是:调用insertRowsAtIndexPaths或deleteRowsAtIndexPaths时,表格 的 行数 一定要与数据源的数量一致。

2.1原因分析1:异步线程更新数据源

下面使用一个Demo来复现这种情况:

- (void)p_addRow {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[NSThread sleepForTimeInterval:0.5]; //模拟网络数据加载
[self.arrData addObject:@"cell number 1 --"]; dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.arrData.count - inSection:]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
});
});
} - (void)p_deleteRow {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[NSThread sleepForTimeInterval:0.5]; //模拟网络数据加载
[self.arrData removeObjectAtIndex:]; dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.arrData.count - inSection:]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
});
});
}

这两个方法调用的时候,我们先按照常规的操作调用,每点击一次,添加/删除一条数据,这种情况下,从实际效果可以看到不会出现崩溃的现象。

现在用一个定时器来模拟手点的效果,经过试验得知,当定时器执行的时间间隔过快,而网络数据反应太慢的情况,会出现崩溃的现象,代码如下:

- (void)leftButton {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(p_deleteRow) userInfo:nil repeats:YES];
} - (void)rightButton {
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(p_addRow) userInfo:nil repeats:NO];
} - (void)p_addRow {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[NSThread sleepForTimeInterval:0.5]; //模拟网络数据加载
[self.arrData addObject:@"cell number 1 --"]; dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.arrData.count - inSection:]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
});
});
} - (void)p_deleteRow {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[NSThread sleepForTimeInterval:0.5]; //模拟网络数据加载
[self.arrData removeObjectAtIndex:]; dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.arrData.count - inSection:]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
});
});
}

崩溃日志如下:

这样看还是有点抽象,我们加一个变量来保存当前表格的行数以及在执行insertRowsAtIndexPaths时数据源的表格行数:

从结果可以看到,由于获取数据是在异步线程里面进行的,导致在执行insertRowsAtIndexPaths时,实际上数据源已经进行了多次插入操作,这样,只执行一次insertRowsAtIndexPaths,便会出现崩溃现象了。

2.2原因分析2:主线程更新数据源

上面小节是在异步线程中更新数据源,这里我们把数据源的放在主线程,看看实际效果:

使用上面的代码,我们可以看到没有出现崩溃的问题,那么是不是表示在主线程更新数据源就没有问题呢?我们试一下这种情况:如果通过网络加载或其他方式加载数据的时候,数据源有多条数据呢?还是看代码演示:

从这里可以看到,如果数据源更新了多条数据,仍然在insertRowsAtIndexPaths方法里面只加了一条数据时,便会出现崩溃现象。

2.3其他情况

上两节的内容分析的崩溃原因,有一个共同点:下面这两个数字是不一样的(删除的话,前面那个数字应该小于后面那个数字,如果不是这个规则,应该是网络又更新了数据源)。

The number of rows contained in an existing section after the update () must be equal to the number of rows contained in that section before the update ()

但从UMeng的崩溃日志看,实际上还有一种情况,那就是这两个数字是一样的。这种情况是怎么发生的呢?我们修改一下代码:

从代码可以看到:更新数据源之后,先调用了一下[self.tableView reloadData],这个时候因为表格数据源已经更新,并且表格也已经更新,这个时候再调用一次insertRowsAtIndexPaths方法,就会接着导致数据源和表格行不一致,从而导致崩溃。

3.解决方案

通过对我们工程代码的分析,数据源的操作是在主线程(对涉及到数据源的地方,都打印了线程的日志信息,显示当前线程为主线程),因此初步判断出现崩溃的原因为2.2小节和2.3小节所描述的。这两种情况,我们先用Demo来演示一下解决方案看看是不是有效的:

逐步放开两个注释内容,可以看到,这三个条件下,都不会崩溃。

同样的,演示一下删除:

逐步放开两个注释内容,可以看到,这三个条件下,也不会崩溃。

一个友盟BUG的思考和分析:Invalid update的更多相关文章

  1. android第三方分享之友盟社会化组件

    前言 现在几乎所有的app都带有分享功能,第一为了更好地推广自己的产品,第二作为使用者也能及时的把自己觉得好的文章,话题,app分享到社交平台供大家一起学习和使用.开发中虽然android系统自带分享 ...

  2. iOS开发-友盟分享(1)

    1.集成友盟分享,需要先注册一个友盟账号,注册地址 友盟开发者平台官网  友盟集成文档 友盟sdk下载地址友盟sdk下载地址 2,成功下载sdk集成后,微信分享需要配置一下 新浪微博 之类到同样配置就 ...

  3. 友盟分享--集成QQ和微信

    随着社交工具的应用范围越来越广,分享一些内容的功能也开始要求实现了. 用得比较多的第三方,比如说友盟,比如说Share等等... 前几天刚用友盟写了集成QQ和微信客户端的功能,觉得有必要分享一下. 在 ...

  4. AppDelegate减负之常用三方封装 - 友盟推送篇

    之前分享过集成友盟推送的方法, 需要的朋友可以查看一下链接: http://www.cnblogs.com/zhouxihi/p/6533058.html 一般开发中我们比较多使用的三方有友盟推送, ...

  5. 线上应用bug跟踪查找-友盟统计

    线上的应用只要用心点点都能发现些bug,连微信,QQ也不列外.但是bug中最严重的算是闪退了,这导致了用户直接不能使用我们的app. 我们公司是特别注重用户反馈和体验的,我们会定期打电话咨询用户的使用 ...

  6. 如何通过友盟分析发布后App崩溃日志-b

    要分析崩溃日志,首先需要保留发布时的编译出来的.xcarchive文件.这个文件包含了.DSYM文件. 我一般的做法是,发布成功后,把这个文件.xcarchive直接提交到代码版本库对应的版本分支里, ...

  7. 如何通过友盟分析发布后App崩溃日志

    http://blog.csdn.net/totogo2010/article/details/39892467 要分析崩溃日志,首先需要保留发布时的编译出来的.xcarchive文件.这个文件包含了 ...

  8. 友盟错误日志分析(转自:COCOACHINA shemy )

      在做的项目中,用到了友盟的组件,在没有禁用错误日志上传之前,收集了一些错误日志. 有一些朋友看到了错误日志,却不知道怎么定位到程序的的代码中,实际上,这一步是非常的简单.友盟没有集成.dSYM文件 ...

  9. 打造高仿QQ的友盟反馈界面(MVP模式)

    什么是MVP呢,简单来说就是将view层和逻辑完全独立出来,让逻辑和显示完全独立.本例中就是采用了这种模式,让activity作为view层,activity中涉及了适配器,所以这里尝试让适配器作为P ...

随机推荐

  1. django在centos部署

    各种坑各种蛋疼,搞了两天终于能用了.整体分2步: 1. 通过uwsgi 启动django项目 在manage.py同级目录,新建uwsgi.ini文件 [uwsgi] # 配置服务器的监听ip和端口, ...

  2. MYSQL性能优化(1)

    优化步骤 1.show status 查询服务器状态运行信息 根据增删改查统计信息可以知道数据库是查询为主还是更新为主,各类型业务大致比例(更新操作 执行与回滚都会计数) 对于事务,可以通过Com_c ...

  3. 深入理解Java中的IO

    深入理解Java中的IO 引言:     对程序语言的设计者来说,创建一个好的输入/输出(I/O)系统是一项艰难的任务 < Thinking in Java >   本文的目录视图如下: ...

  4. VideoView 监听视频格式不支持时的错误。

    视频播放格式不支持的处理https://www.cnblogs.com/ygj0930/p/7737209.html 不处理的情况下,默认会有弹框提示:不支持该视频格式. mVideoView.set ...

  5. 《CSAPP》符号和符号表

    符号和符号表 每个可重定位目标模块m都有一个符号表,它包含m所定义和引用的符号的信息. 有三种不同的符号: 由m定义并能被其他模块引用的全局符号.对应非静态的C函数以及不带C static属性的全局变 ...

  6. C++中的覆盖与隐藏(详细讲解)

    C++类中覆盖与隐藏一直是一个容易理解出错的地方,接下来我就详细讲解一下区别在何处 覆盖指的是子类覆盖父类函数(被覆盖),特征是: 1.分别位于子类和父类中 2.函数名字与参数都相同 3.父类的函数是 ...

  7. [Java学习]多线程

    关于多进程与多线程 使用多进程的目的:提高CPU利用率. 使用多线程的目的:提高应用程序?利用率. 多线程与多进程区别:进程间内存独立:同一个进程的线程间共享"堆内存和方法区内存" ...

  8. tp3.2单函数总结

    A($name,$layer='',$level=0) // 实例化多层控制器 格式:[资源://][模块/]控制器         B($name, $tag='',&$params=NUL ...

  9. Git常用命令及场景

    Git命令推送到远程分支 1.登录GitHub创建一个远程仓库. https://github.com 2.git init 本地创建一个目录,并初始化一个git仓库. 3.git add 添加文件到 ...

  10. android端如何实现设置颜色透明度?

    今 天测试反馈设置的色值跟设计图不一致,其实是一个很简单的设置,黑色,70%透明. 而我是这么设置的:<solid android:color="#30000000"/> ...