在这里插入图片描述

iOS crash 问题分析

  • iOS crash 常用分析工具
    • IOS 崩溃日志
  • iOS crash 原因分析
    • 1.调用悬浮指针
    • 2.数组越界访问
    • 3.调用了未实现的方法
    • 4.调用的库函数版本高于本机
    • 5.返回空cell
    • 6.类释放时未remove通知,之后收到通知
    • 7.类释放时delegate未置空,之后被回调
    • 8.使用nil做初始化操作
      • a.字典赋值,取值
      • b. 数组赋值,取值
    • 9.NSRange访问越界,
    • 10.对象对应关系异常。例如a实例removeObserver一个非a类关联的监听对象。
      • 10.1 kvo 不对称导致闪退
    • 11.delegate先于tableview被置空,后收到关于table或者scroll的调用.
    • 12.系统内存不足。
    • 13. Block 块,循环引用
    • 14. Unity 导致ios app 闪退
      • 1. Unity打包iOS的应用AirPlay投屏闪退问题
    • 15. UI 问题导致闪退
      • 15.1 动画问题
      • 15.2 UINavigationBar问题
      • 15.3 输入法闪退
      • 15.4 UITableView闪退问题
      • 15.5 UICollectView闪退问题
      • 15.6 在次线程中更新UI
    • 16. ios api使用没有考虑兼容导致闪退
    • 17. ios 方法延迟执行,定时器调用方法是,对象已经被释放导致闪退
      • 17.1 定时器问题
      • 17.2 延迟调用或delete 方法延迟调用时,对象已经被释放
    • 18. ios 通知处理不当,导致闪退
      • 18.1 通知没有移除
    • 19. 本地存储的数据结构改变
    • 20. 访问的数据为空或访问数据类型不对
    • 21. 操作了不该操作的对象,野指针之类
    • 22. 内存处理不当
    • 23. 主线程UI长时间卡死,被系统杀掉
    • 24. 多线程之间切换访问引起的crash
    • 25.
  • iOS bugly Crash问题汇总
    • 1. 线程问题
    • 2. delegate问题
      • 2. 1 webview delegate未清空导致闪退
  • iOS项目中 bugly Crash问题汇总
    • 1. 与unity相关的crash
    • 2. 比较隐蔽的Crash-----内存暴涨,最后被系统杀死
    • 3. 插入数据库失败,读出模型为空,强解包导致Crash
    • 4. 循坏引用问题
    • 5. 蓝牙命令数组下标越界,导致crash
    • 6. UI跳转页面强解包导致Crash
    • 7. Bool类型强解包导致Crash
    • 8. 分母为0,导致Crash
    • 9. 音频播放结束,强解包,导致Crash
    • 10. 二维码扫描页面,对象已经释放,强解包,导致Crash
    • 11. Pop页面时对象已经释放,强解包,导致Crash
    • 12. UI对象已经释放,强解包,导致Crash

S crash 问题分析)

iOS crash 常用分析工具

IOS 崩溃日志

  • xcode中查看崩溃信息

xcode->Window->Organizer->Crashes

  • 根据符号表来监测奔溃位置

什么是符号表

符号表就是指在Xcode项目编译后,在编译生成的二进制文件.app的同级目录下生成的同名的.dSYM文件。
.dSYM文件其实是一个目录,在子目录中包含了一个16进制的保存函数地址映射信息的中转文件,所有Debug的symbols都在这个文件中(包括文件名、函数名、行号等),所以也称之为调试符号信息文件。

符号表有什么用

符号表就是用来符号化 crash log(崩溃日志)。crash log中有一些方法16进制的内存地址等,通过符号表就能找到对应的能够直观看到的方法名之类。

如何得到.dsYM文件

我们在Archive的时候会生成.xcarchive文件,然后显示包内容就能够在里面找到.dsYM文件和.app文件。

如何使用.dsYM

  1. 友盟.dsYM分析

如果是使用友盟的话,我们能在错误列表里看到一些错误,然后可以导出奔溃信息,导出的文件为.csv文件。友盟有一个分析工具,使用那个工具可以看到一些错误的函数,行号等。但是很容易分析失败,不知道为什么?
注意:使用的时候要确保你的.xcarchive在 ~/Library/Developer/Xcode/或该路径的子目录下。
.xcarchive里的.dsYM文件和.app文件是有对应的UUID的。然后你的错误详情里也是有UUID,只有当UUID相等时才能分析对。
我犯的错误:因为我们是两个人开发,Archive的时候都是在另一个人的电脑上Archive的,所以我的电脑里根本没有对应的.xcarchive文件。所以我在我电脑上用友盟的分析工具分析是时候是监测不出来错误的。

  1. 第三方小工具.dsYM分析

或者自己找到.xcarchive文件和错误内存地址(友盟错误详情里标绿色的为错误内存地址)。然后通过一个小应用来分析出对应的函数。应用下载地址,具体可参考文章dSYM 文件分析工具
下图是我友盟里的错误信息,可以分析的内存地址就是标绿的地方,图中zhefengle就是你的app名,这部分后面的地址就是可以解析符号化的地址:

下图是分析工具分析上面的错误内存地址:

  1. 命令行工具symbolicatecrash

symbolicatecrash是xcode的一个符号化crash log的命令行工具。使用方法也就是导出.crash文件(crash log)和找到.dsYM文件,然后进行分析。
如何使用查看[iOS]使用symbolicatecrash分析crash文件: 点击此处查看symbolicatecrash分析crash文件

  1. 还有命令行工具atos

如果你有多个“.ipa”文件,多个".dSYMB"文件,你并不太确定到底“dSYM”文件对应哪个".ipa"文件,那么,这个方法就非常适合你。
特别当你的应用发布到多个渠道的时候,你需要对不同渠道的crash文件,写一个自动化的分析脚本的时候,这个方法就极其有用。
简单使用方法命令行工具atos: 点击此处查看工具atos

  • 奔溃日志分析

参考 iOS应用崩溃日志分析里面有很详细的分析介绍。

崩溃日志
以上是一个完整的崩溃日志。其实友盟错误详情里的就是上图的第4部分。

如何得到崩溃日志

  1. 把设备连上电脑,得到自己设备的崩溃日志

崩溃日志可以从xcode里打开Devices看到对应手机的一些奔溃信息。点击下图的View Device Logs就能看到崩溃日志。

  1. 使用第三方崩溃管理工具

友盟, 听云, 腾讯bugly

  1. 自己截取崩溃日志
    自己写入代码,然后截取到崩溃日志,把崩溃日志发送到开发者邮箱里。
    iOS Crash(崩溃)调试技巧这篇文章中有介绍如何截取崩溃日志并发送到邮箱。

分析崩溃日志

崩溃日志中的(3)异常

Exception Type异常类型
通常包含1.7中的Signal信号和EXC_BAD_ACCESS。Exception Codes:异常编码
0x8badf00d: 读做 “ate bad food”! (把数字换成字母,是不是很像 :p)该编码表示应用是因为发生watchdog超时而被iOS终止的。 通常是应用花费太多时间而无法启动、终止或响应用系统事件。0xbad22222: 该编码表示 VoIP 应用因为过于频繁重启而被终止。0xdead10cc: 读做 “dead lock”!该代码表明应用因为在后台运行时占用系统资源,如通讯录数据库不释放而被终止 。0xdeadfa11: 读做 “dead fall”! 该代码表示应用是被用户强制退出的。根据苹果文档, 强制退出发生在用户长按开关按钮直到出现 “滑动来关机”, 然后长按 Home按钮。强制退出将产生 包含0xdeadfa11 异常编码的崩溃日志, 因为大多数是强制退出是因为应用阻塞了界面。

崩溃日志中的(4)线程回溯

这部分提供应用中所有线程的回溯日志。 回溯是闪退发生时所有活动帧清单。它包含闪退发生时调用函数的清单。看下面这行日志:

它包括四列:
帧编号—— 此处是2。(数子从大到小为发生的顺序)
二进制库的名称 ——此处是 XYZLib.
调用方法的地址 ——此处是 0x34648e88.
第四列分为两个子列,一个基本地址和一个偏移量。此处是0×83000 + 8740, 第一个数字指向文件,第二个数字指向文件中的代码行。

  • 野指针分析方法(Enable Malloc Scribble)

因为野指针的原因发生崩溃是常常出现的事,而且比较随机。关于一些原因及概念后面我们会讲到。所以我们要提高野指针的崩溃率好来帮我们快速找到有问题的代码。

对象释放后只有出现被随机填入的数据是不可访问的时候才会必现Crash。

这个地方我们可以做一下手脚,把这一随机的过程变成不随机的过程。对象释放后在内存上填上不可访问的数据,其实这种技术其实一直都有,xcode的Enable Scribble就是这个作用。

更加详细的介绍可以参考:如何定位Obj-C野指针随机Crash。

DSCrashDemo这个demo:点击下载 里有上面这篇文章里写的关于提高野指针崩溃率的例子。

  • 僵尸模式(NSZombieEnabled)

启用了NSZombieEnabled的话,它会用一个僵尸来替换默认的dealloc实现,也就是在引用计数降到0时,该僵尸实现会将该对象转换成僵尸对象。僵尸对象的作用是在你向它发送消息时,它会显示一段日志并自动跳入调试器。
所以当启用NSZombieEnabled时,一个错误的内存访问就会变成一条无法识别的消息发送给僵尸对象。僵尸对象会显示接受到得信息,然后跳入调试器,这样你就可以查看到底是哪里出了问题。
所以这时一般崩溃的原因是:调用了已经释放的内存空间,或者说重复释放了某个地址空间。

如何找出问题

  1. NSZombieEnabled

打开NSZombieEnabled之后,如果遇到对应的崩溃类型既调用了已经释放的内存空间,或者说重复释放了某个地址空间。那么就能在GDB中看到对应的输出信息。
比如会出现如下这样的问题:
[__NSArrayM addObject:]: message sent to deallocated instance 0x7179910

  1. MallocStackLoggingNoCompact

如果崩溃是发生在当前调用栈,通过上面的做法,系统就会把崩溃原因定位到具体代码中。但是,如果崩溃不在当前调用栈,系统就仅仅只能把崩溃地址告诉我们,而没办法定位到具体代码,这样我们也没法去修改错误。这时就可以修改scheme,让xcode记录每个地址alloc的历史,这样我们就可以用命令把这个地址还原出来。
如图:(跟设置NSZombieEnabled一样,添加MallocStackLoggingNoCompact,并且设置为YES)

这样,当出现崩溃原因是message sent to deallocated instance 0x7179910,我们可以使用以下命令,把内存地址还原:

(gdb) nfo malloc-history 0x7179910

也可以使用下面的命令
(gdb) shell malloc_history {pid/partial-process-name} {address}

这篇文章中有介绍MallocStackLoggingNoCompact的使用:点击此处查看。

还有官方文档Enabling the Malloc Debugging Features介绍了类似NSZombieEnabled和MallocStackLoggingNoCompact这类的环境变量的作用。

TODO:翻译Enabling the Malloc Debugging Features这篇文章,写对应的demo测试这类变量设置后如何找出内存出错问题。

  • Enable Address Sanitizer(地址消毒剂)

设置这个参数后就能看到一些更详细的错误信息提示,甚至会有内存使用情况的展示。

C语言是一门危险的语言,内存安全是一个主要的问题。C语言中根本没有内存安全可言。像下面的代码,会被正常的编译,而且可能正常运行:
char *ptr = malloc(5);
ptr[12] = 0;
对于内存安全的验证已经有一些解决方案了。如Clang的静态代码分析,可以从代码中查找特定类型的内存安全问题。如Valgrind之类的程序可以在运行时检测到不安全的内存访问。

Address Sanitizer是另外一种解决方案。它使用了一种新的方法,有利有弊。但仍不失为一个查找代码问题的有力工具。

这类工具的理论依据是:访问内存时,通过比较访问的内存和程序实际分配的内存,验证内存访问的有效性,从而在bug发生时就检测到它们,而不会等到副作用产生时才有所察觉。

malloc函数总是最少分配16个字节。为了储存针对标准malloc的内存的保护,需要分配内存到16字节的范围内,因此,若分配的内存大小不是16字节的整数倍,余出的几个字节将不受保护。

Address Sanitizer会追踪受限内存,使用了一种简单但是很巧妙的方法:它在进程的内存空间上保存了一个固定的区域,叫做“影子内存区”。用内存消毒剂的术语来说,一个被标记为受限的内存被称作“中毒”内存。“影子内存区”会记录哪些内存字节是中毒的。通过一个简单的公式,可以将进程中的内存空间映射到“影子内存区”中,即:每8字节的正常内存块映射到一个字节的影子内存上。在影子内存上,会跟踪这8字节的“中毒状态”。

Address Sanitizer这篇文章详细介绍了Enable Address Sanitizer,对应的中文翻译在 Xcode 7上直接使用Clang Address Sanitizer

  • Static Analyzer(静态分析)

Static Analyzer是一个非常好的工具去发现编译器警告不会提示的问题和一些个人的内错泄露和死存储(不会用到的赋了值的变量)错误。这个方法可能大大的提高内存使用和性能,以及提升应用的整体稳定性和代码质量。

打开方式:Xcode->Product-Analyze
然后我们就能看到如下蓝色箭头所示的一些有问题的代码。

unrecognized selector send to instancd 快速定位

在debug navigator的断点栏里添加Create Symbolic Breakpoint。

在Symbolic中填写如下方法签名:
-[NSObject(NSObject) doesNotRecognizeSelector:]

设置完成后再遇到类似的错误就会定位到具体的代码。

1.7.Signal和EXC_BAD_ACCESS错误分析

1.7.1.Signal

什么是Signal

在计算机科学中,信号(英语:Signals)是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
在iOS中就是未被捕获的Objective-C异常(NSException),导致程序向自身发送了SIGABRT信号而崩溃。

Signal信号的类型

SIGABRT–程序中止命令中止信号
SIGALRM–程序超时信号
SIGFPE–程序浮点异常信号
SIGILL–程序非法指令信号
SIGHUP–程序终端中止信号
SIGINT–程序键盘中断信号
SIGKILL–程序结束接收中止信号
SIGTERM–程序kill中止信号
SIGSTOP–程序键盘中止信号
SIGSEGV–程序无效内存中止信号
SIGBUS–程序内存字节未对齐中止信号
SIGPIPE–程序Socket发送失败中止信号
iOS异常捕获这篇文章中有对各种信号的解释。

SIGABRT

就crash而言,SIGABRT是一个比较好解决的,因为他是一个可掌控的crash。App会在一个目的地终止,因为系统意识到app做了一些他不能支持的事情。
通常, SIGABRT 异常是由于某个对象接收到未实现的消息引起的。 或者,用简单的话说,在某个对象上调用了不存在的方法。

SIGSEGV

SIGSEGV程序无效内存中止信号,一般是表示内存不合法,

SIGBUS

SIGBUS程序内存字节未对齐中止信号,
截取Signal和Exception从容的崩溃
DSSignalHandlerDemo:点击此处下载
这是一个防止奔溃的源码,可以使一些原本会奔溃的操作弹出UIAlertView。里面写了两种信号量的崩溃:SIGSEGV、SIGABRT,还有一些信号大家可以写上去提个PR给我。

下图为源码的运行图,其中Signal中的Signal(EGV)第一次点击的时候能弹出alert,如果第二次点击就没有崩溃和alert,感觉像卡死一样。

而Signal(BRT)中的这种信号错误多次点击也是没有问题的还是能继续下去。个人猜测可能是SIGSEGV这种信号错误会导致了整个进程挂了。

注意:测试的时候如果测试Signal类型的崩溃,不要在xcode下的debug模式进行测试。因为系统的debug会优先去拦截。应该build好应用之后直接点击运行app进行测试。

1.7.2.EXC_BAD_ACCESS

EXC_BAD_ACCESS是一个比较难处理的crash了,当一个app进入一种毁坏的状态,通常是由于内存管理问题而引起的时,就会出现出现这样的crash。通常1.7.1中的Signal信号错误都会提醒EXC_BAD_ACCESS。

iOS crash 原因分析

ANR:程序为何会出现“无响应”的状态。在iOS应用中,所有的UI操作及更新都是在主线程完成,并且主线程的runloop是逐个处理用户事件的(当然其他的runloop也一样),所以主线程必须等待上一次事件处理完成后才能继续响应下一次事件。绝大部分用户感知到的卡顿就是由于主线程阻塞了,在处理某次事件消耗了过长的时间,导致主线程处于等待状态,无法及时响应用户的下一次输入事件。由于iOS 上的 UIKit 只能在主线程进行处理,导致开发者在开发过程中不经意间在主线程做了一些消耗时间的工作,导致了应用卡顿。
避免卡顿的黄金法则就是不要让主线程干重活,例如网络请求,读写大文件,复杂的运算 等一些耗费大量系统资源及时间的任务。充分利用好 iOS 的多线程,如 NSThread、NSO peration Queue,GCD 等干脏活,累活,让主线程能及时迅速的响应用户事件。

Crash是用户使用应用的过程中最糟糕的体验,也是程序员最深恶痛绝的BUG。然而,每位程序员都免不了接触crash事件。应用Crash的产生主要有以下原因:

1.调用悬浮指针

2.数组越界访问

(1)section (2) beyond bounds (1). row (1) beyond bounds (1) for section (0).’
手动创建NSIndexPath时row和section越界,如果该indexPath不使用也不会crash,在调用:
[_mainTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];

解决方法:
根据tableView的属性和方法算出section和row的个数进行判断

@property (nonatomic, readonly) NSInteger numberOfSections;

  • (NSInteger)numberOfRowsInSection:(NSInteger)section;

if ([_mainTableView numberOfSections] > section && [_mainTableView numberOfRowsInSection:section] > row) {
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:section];
}

3.调用了未实现的方法

unrecognized selector sent to instance

(1) xib连线的方法未实现,第一次连线后代码中将方法删除,控件中没有删除
this class is not key value coding-compliant for the key lblBut.
控件属性重复拉线,一个控件拉出两个属性,删除其中一个
不可变字典调用了setValueForKey,调用方法前看清是不是可变字典

(2) 代理未实现@optional代理方法
(3) 响应 delegate 的VC,或者 View 的实例被提前释放了
解决方法:
if (self.delegate && [self.delegate respondsToSelector:@selector(xxx)])

(4)无效参数:
Invalid parameter not satisfying: date

给datePicker赋值的NSDate为nil,可能因为日期格式化字符串不对,由dateFormatter生成的date为nil,尽量用系统方法计算日期。
解决方法:

if (date) {
_dpBirth.date = date;
}

4.调用的库函数版本高于本机

5.返回空cell

6.类释放时未remove通知,之后收到通知

6.1 kvo

1).An instance of class ViewController was deallocated while key value observers were still registered with it
kvo没有移除监听,或者kvo多次注册监听
(2)Cannot remove an observer ViewController for the key path “key” from ViewController because it is not registered as an observer
移除的key和注册的key不一致,或者移除多次
解决方法:
确保注册一次要移除一次,不要重复注册,不要重复移除

7.类释放时delegate未置空,之后被回调

(1)延迟调用代理方法时delegate为nil(ios7)

 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"xxx" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];[alert show];[self performSelector:@selector(pop) withObject:nil afterDelay:2];- (void)pop {[self.navigationController popViewControllerAnimated:YES];
}

解决方法:
alertView如果不处理点击事件要将delegate设为nil,如果涉及到跳转的先处理完代理事件在跳转页面

(2) webview delegate 在窗口销毁时没有清空,在ios 9会闪退

        var wv = WKWebView(frame: UIScreen.main.bounds, configuration: wkWebConfig)wv.navigationDelegate = selfwv.scrollView.delegate = self

如果窗口销毁时,没有将delegate设置为nil,回导致卡死或者闪退

8.使用nil做初始化操作

a.字典赋值,取值

例如:NSStringstr =nil;
NSDictionary
dic =@{@“name”?“emma”,@“age”:str};


//赋值
setObjectForKey key不能为nil,object不能为nil
setValueForKey key不能为nil,value可以为nil
dic[key] = value; key不能为nil,value可以为nil
Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
//取值
[dic objectForKey:key]; key可以为nil
[dic valueForKey:key]; key也可以为nil
//在使用@{@”key”:value} 这种方式初始化的时候,一定要对value做是否为nil的判断,为nil就不要加入Dictionary,否则会挂if (key.length > 0) {[dic setValue:value forKey:key];}if ([arrKeys containsObject:key]) {[dic setValue:value forKey:key];}//或使用标准的初始化方法:
[NSDictionary dictionaryWithObjectsAndKeys:value1,@"v1",value2,@"v2",value3,@"v3", nil];
//或其它的几个初始化方法进行初始化,但是这个方法如果value为nil就初始化的字典也为nil,也是一个坑,你可能以为字典不为nil,仅仅是这个key和value不存在。
//关联:
//使用@[]方法初始化NSArray也有此坑,规避方法同字典一样
b. 数组赋值,取值

取值

(1)取值范围越界
index 0 beyond bounds for empty NSArray

if(index < arr. count) {[arr objectAtIndex:index];arr[obj];
}

存值

(1)插入nil
attempt to insert nil object from objects[0]
(2)插入范围越界
index 1 beyond bounds for empty array

if(obj && index < arr. count) {[arr insertObject:obj atIndex:index];
}

9.NSRange访问越界,

Range or index out of bounds:
构造range时,使用NSMakeRange时loc或者len越界,如果该range不使用也不会crash,在调用这两个方法时会crash:
substringWithRange:
replaceCharactersInRange:
解决方法:
if (loc < textString.length && len <= textString.length - loc) {
NSMakeRange(loc, len);
}

例如:NSString *str = @“abcedfh”;
NSRange range = NSMakeRange(5, 9);
[str substringWithRange:range];

10.对象对应关系异常。例如a实例removeObserver一个非a类关联的监听对象。

10.1 kvo 不对称导致闪退

(1).An instance of class ViewController was deallocated while key value observers were still registered with it
kvo没有移除监听,或者kvo多次注册监听
(2)Cannot remove an observer ViewController for the key path “key” from ViewController because it is not registered as an observer
移除的key和注册的key不一致,或者移除多次
解决方法:
确保注册一次要移除一次,不要重复注册,不要重复移除

11.delegate先于tableview被置空,后收到关于table或者scroll的调用.

12.系统内存不足。

对于Crash的解决也都不同,一般必现的Crash可以通过对代码仔细的阅读辅以断点调试几乎都可以找出来。对于隐藏较深的Crash问题可以通过以下手段。
A : 企业内部测试阶段:找到发包时的.ipa文件以及打包时archive生成的.dSYM文件以及应用使用过程中产生的.crash文件。通过对这三个文件的解析。可以查看应用程序具体的崩溃原因,以及崩溃什么地方。
B:应用已经安装到用户手机中,此时的crash可以通过一些第三方的工具进行获取,常用的有Bugly、Testin、友盟等。但是需要在研发的过程中将这些第三方的SDK集成到自己的代码中。

13. Block 块,循环引用

(1) block作为属性时要用copy
@property (copy,nonatomic)block;

(2) 调用block需要判断是否为空,不然会报坏内存访问
if (block) {
block();
}

14. Unity 导致ios app 闪退

1. Unity打包iOS的应用AirPlay投屏闪退问题

如果打开游戏之前已经连接上AirPlay既会崩溃,相反则不会;
另外 在Unity打包的Xcode工程目录下有一与投屏相关DisplayManager.mm代码文件

以下是投屏连接相关的代码:

- (void)screenDidConnect:(NSNotification*)notification
{[self registerScreen: (UIScreen*)[notification object]];[self updateDisplayListInUnity];
}

只需要注释掉方法中的代码即可解决崩溃(方法作用感兴趣的可以去看一下源代码)。
即:

  • (void)screenDidConnect:(NSNotification*)notification
    {
    //[self registerScreen: (UIScreen*)[notification object]];
    //[self updateDisplayListInUnity];
    }

unity3d 游戏 ios提审: Your app crashed on iPad or iPhone running iOS 11.2.5 connected to an IPv6 network during our review. ios日志: Hardware Model: iPad4,4 Code Type: ARM-64 (Native) OS Version: iPhone OS 11.2.5 (15D60) Exception Type: EXC_BAD_ACCESS (SIGSEGV)

0.UnityUpdateDisplayList + 21969364 (LibEntryPoint.mm:570)

1.UnityUpdateDisplayList + 21969364 (LibEntryPoint.mm:570)

2.[DisplayManager updateDisplayListInUnity] + 72732 (DisplayManager.mm:330)

3.[DisplayManager screenDidConnect:] + 73664 (DisplayManager.mm:369)

.

27.main + 22252 (main.mm:32)

这是开启Airplay引起的崩溃

该问题在5.6,2017.1,2017.2,2017.3,2018版本都有修复,但是5.5之前的版本(包括5.5)没有补丁版本,只能通过关闭airplay方法修复

在Xcode工程中,找到Classes/Unity/DisplayManager.mm文件。

在该文件中有如下代码:

- (id)init{if ((self = [super init])){[[NSNotificationCenter defaultCenter] addObserver: selfselector: @selector(screenDidConnect:)name: UIScreenDidConnectNotificationobject: nil];[[NSNotificationCenter defaultCenter] addObserver: selfselector: @selector(screenDidDisconnect:)name: UIScreenDidDisconnectNotificationobject: nil];<...>把 [[NSNotificationCenter defaultCenter] addObserver: selfselector: @selector(screenDidConnect:)name: UIScreenDidConnectNotificationobject: nil];[[NSNotificationCenter defaultCenter] addObserver: selfselector: @selector(screenDidDisconnect:)name: UIScreenDidDisconnectNotificationobject: nil];

代码注释掉就可以解决崩溃的问题。

15. UI 问题导致闪退

15.1 动画问题

Trapped uncaught exception ‘CALayerInvalidGeometry’, reason: ‘CALayer bounds contains NaN: [0 nan; 320 0]’
当用float接收的运算中分母为0,并将该结果放在CGRectMake、CGSizeMake或者CGPointMake中使用,计算结果不使用也不会crash,如果用nsinteger接收也不会crash
c = 0;
float a = b/c; (会crash)
NSInteger a = b/c; (不会crash)
CGRectMake(0,a,width,height);
解决方法:
进行运算时分母不能为0

15.2 UINavigationBar问题

Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Auto Layout still required after executing -layoutSubviews. UINavigationBar’s implementation of -layoutSubviews needs to call super.’
原因:
再给导航栏设置left和rightItem时使用下面的方法在iOS7中可能出现闪退,原因未知。
UIBarButtonItem *leftBar = [[UIBarButtonItem alloc] initWithCustomView:_btnBack];
self.navigationItem.leftBarButtonItem = leftBar;

解决方法:
使用下面的方法代替:
UIBarButtonItem *rightBar = [[UIBarButtonItem alloc] initWithTitle:@“确定” style:UIBarButtonItemStylePlain target:self action:@selector(didPressedFinish)];
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:negativeSpacer, rightBar, nil];

//当str为nil时会奔溃
[self stringByAppendingString:str];

当str为nil时会奔溃
NSMutableAttributedString *sTitle = [[NSMutableAttributedString alloc] initWithString:str];

15.3 输入法闪退

UIKBBlurredKeyView candidateList:unrecognized…
原因,当项目中引入的分类重写了UITouchBegans、move、end方法,用户在使用手写输入法时会出现闪退
解决方法:将分类中的这三个方法注释掉。

15.4 UITableView闪退问题
15.5 UICollectView闪退问题
  1. UICollectionView没有注册cell
    报错:
    Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘could not dequeue a view of kind: UICollectionElementKindCell with identifier ZTPhotoCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard’

  2. UIConllectionView的cellForItemAtIndexPath代理方法返回了nil
    报错:
    Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: 'the collection view’s data source did not return a valid cell from -collectionView:cellForItemAtIndexPath: for index path

15.6 在次线程中更新UI

大家都知道一个规则就是,UI更新只能在主线程中,如果不在主线程中更新UI,会导致一些问题,有时候会导致闪退问题。

先来分析一个bugly上报的Crash问题:

问题原因是,在次线程更新UI,用SnapKit更新布局时,SnapKit框架会抛出fataerror异常,应用闪退。

为什么不能在次线程中更新UI呢?

像UIKit这样大的框架上确保线程安全是一个重大的任务,会带来巨大的成本。UIKit不是线程安全的,假如在两个线程中设置了同一张背景图片,很有可能就会由于背景图片被释放两次,使得程序崩溃。或者某一个线程中遍历找寻某个subView,然而在另一个线程中删除了该subView,那么就会造成错乱。apple有对大部分的绘图方法和诸如UIColor等类改写成线程安全可用,可还是建议将UI操作保证在主线程中。
事实上在子线程中如果要对其他UI 进行更新,必须等到该子线程运行结束,而对响应用户点击的Button的UI更新则是及时的,不管他是在主线程还是在子线程中做的更新,意义都不大了,因为子线程中对所有其他ui更新都要等到该子线程生命周期结束才进行。
在子线程中是不能进行UI 更新的,我们看到的UI更新其实是子线程代码执行完毕了,又自动进入到了主线程,执行了子线程中的UI更新的函数栈,这中间的时间非常的短,就让大家误以为分线程可以更新UI。如果子线程一直在运行,则子线程中的UI更新的函数栈 主线程无法获知,即无法更新。只有极少数的UI能直接进行UI更新,因为开辟线程时会获取当前环境,如点击某个按钮,这个按钮响应的方法是开辟一个子线程,在子线程中对该按钮进行UI 更新是能及时的,如上面的换背景图,但这没有任何意义。

16. ios api使用没有考虑兼容导致闪退

新老操作系统兼容

开发人员在进行开发的时候,常常使用的是某个操作系统版本,所以在开发人员进行开发测试的那个系统版本上基本不会出现问题。但在其他版本上开发人员无法进行完全的测试,这就导致了在新系统上运行正常,但在旧系统上却崩溃的情况。
在新 iOS 上正常的应用,到了老版本 iOS 上秒退最常见原因是系统动态链接库或Framework无法找到。这种情况通常是由于 App 引用了一个新版操作系统里的动态库(或者某动态库的新版本)或只有新 iOS 支持的 Framework,而又没有对老系统进行测试,于是当 App 运行在老系统上时便由于找不到而秒退。
还有就是有些方法是新版操作系统才支持的,而又没有对该方法是否存在于老系统中做出判断。这种情况其实还是比较难出现的,除非开发人员太low了,因为这类方法在xcode编码时编辑器都会有提醒的。

解决

这种问题一般就是用户升级操作系统或者开发人员修改问题以兼容老系统。
使用高版本的api,如alertViewController(iOS8)

17. ios 方法延迟执行,定时器调用方法是,对象已经被释放导致闪退

17.1 定时器问题

使用NSTimer 的target被释放了,而timer还去执行定时方法,回调的时候找不到对象就会挂掉
在viewWillDisappear里将[timer invalid],并将timer = nil;

17.2 延迟调用或delete 方法延迟调用时,对象已经被释放

An Objective-C message was sent to a deallocated ‘xxx’ object (zombie) at address
ViewController respondsToSelector:message sent to deallocated instance
原因:delegate的targetvc被提前释放,导致调用代理方法时访问了僵尸对象,有可能是delegate的属性没有设为weak而是assign,或者delegate的属性是weak但是使用的时候却没有用self.delegate调用,可能直接使用了成员变量

18. ios 通知处理不当,导致闪退

18.1 通知没有移除

Thread 1: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
控制台打印
-[UIView requestWillSendCallback:]: message sent to deallocated instance 0x7d554c20

19. 本地存储的数据结构改变

原因

程序在升级时,修改了本地存储的数据结构,但是对用户既存的旧数据没有做好升级,结果导致初始化时因为无法正确读取用户数据而秒退。

解决

第一种:是把服务端传过来的一些信息保存在本地,使用的时候从本地数据库取。

刚开始的时候我是第一次从服务端得到数据的时候直接解析成对应的model然后存入plist文件里面。这时就有一个问题,比如服务端新传了字段newId,但是我旧版model里面没有定义过,存入本地的数据还是没有这个字段。
然后等我升级了程序,新程序里model,定义了这个newId字段,但是旧版里面数据已经保存过一遍了没有这个字段。这时再去取就取不到了。
所以后来我就把存储时解析数据改成了读取时解析数据。就是不管服务端传什么数据都把它存下来,然后在使用的时候再把它解析成对应的model,这样就不会丢失字段了。

第二种:自己的一些数据存储在本地SQLlite,新版的时候表结构改了。

SQLlite只支持更改一个表的名字,或者向表中增加一个字段(列),但是我们不能删除一个已经存在的字段,或者更改一个已经存在的字段的名称、数据类型、限定符等等。
这种就是有时候新版又添加字段了,或者改变了字段的名称了。一般来说原有的字段名称不应该改变,但是添加新字段是常有的事。
一般做法是在第一次创建表的时候加一些冗余字段,以防后面不时之需。但是如果真没办法需要在旧表上增加新字段了,那就要做数据迁移了。
这里有一个库在FMDB基础上管理SQLlite数据库了,可以用来做数据迁移用。FMDBMigrationManager

20. 访问的数据为空或访问数据类型不对

原因

这类情况是比较常见的,后端传回了空数据,客户端没有做对应的判断继续执行下去了,这样就产生了crash。或者自己本地的某个数据为空数据而去使用了。还有就是访问的数据类型不是期望的数据类型而产生崩溃。

解决

第一种:

服务端都加入默认值,不返回空内容或无key,但是服务端往往会不太愿意改,还有就是有些确实应该无值的话key也不用传,减少数据量的传输。

第二种:

这种就是客户端自己做判断,如果每次都是自己去if判断是否为空或格式是否正确那肯定是比较麻烦的。所以这里用到了NSArray和NSDictionary的Category。一般我们访问的数据都是NSArray或NSDictionary,所以在取值方法里面做一下判断,返回正确的数据类型或默认值即可。

DSCategories 这里面有两个CategoryNSArray+SafeAccessM和NSDictionary+SafeAccess可以看一下。

21. 操作了不该操作的对象,野指针之类

  1. 野指针介绍
    iOS中有空指针和野指针两种概念。

空指针是没有存储任何内存地址的指针。如Student s1 = NULL;和Student s2 = nil;

而野指针是指指向一个已删除的对象("垃圾"内存既不可用内存)或未申请访问受限内存区域的指针。野指针是比较危险的。因为野指针指向的对象已经被释放了,不能用了,你再给被释放的对象发送消息就是违法的,所以会崩溃。

空指针和野指针 这篇文章介绍了空指针和野指针的概念。

  1. 野指针崩溃情况
    野指针访问已经释放的对象crash其实不是必现的,因为dealloc执行后只是告诉系统,这片内存我不用了,而系统并没有就让这片内存不能访问。

所以野指针的奔溃是比较随机的,你在测试的时候可能没发生crash,但是用户在使用的时候就可能发生crash了。

注意:arc环境比非arc环境更少出现野指针。

现实出现问题大概是下面几种可能的情况:

  1. 对象释放后内存没被改动过,原来的内存保存完好,可能不Crash或者出现逻辑错误(随机Crash)。
  2. 对象释放后内存没被改动过,但是它自己析构的时候已经删掉某些必要的东西,可能不Crash、Crash在访问依赖的对象比如类成员上、出现逻辑错误(随机Crash)。
  3. 对象释放后内存被改动过,写上了不可访问的数据,直接就出错了很可能Crash在objc_msgSend上面(必现Crash,常见)。
  4. 对象释放后内存被改动过,写上了可以访问的数据,可能不Crash、出现逻辑错误、间接访问到不可访问的数据(随机Crash)。
  5. 对象释放后内存被改动过,写上了可以访问的数据,但是再次访问的时候执行的代码把别的数据写坏了,遇到这种Crash只能哭了(随机Crash,难度大,概率低)!!
  6. 对象释放后再次release(几乎是必现Crash,但也有例外,很常见)。

参考下面这张图:

22. 内存处理不当

说到因为内存处理不当崩溃就要涉及到内存管理问题了。内存管理是软件开发中一个重要的课题。iOS自从引入ARC机制后,对于内存的管理开发者好像轻松了很多,但是还会发生一些内存泄露之类的问题。

对于这一块知识点需要了解ARC的一些机制,还有用instruments排查内存泄露问题等。

23. 主线程UI长时间卡死,被系统杀掉

主线程被卡住是非常常见的场景,具体表现就是程序不响应任何的UI交互。这时按下调试的暂停按钮,查看堆栈,就可以看到是到底是死锁、死循环等,导致UI线程被卡住。

这部分需要研究多线程,还有如何看调试栏里的线程的信息。

GCD死锁这篇文章介绍了GCD使用多线程时的死锁问题。
还有这篇 iOS多线程中,队列和执行的排列组合结果分析 也值得参考

24. 多线程之间切换访问引起的crash

25.

iOS bugly Crash问题汇总

1. 线程问题

  1. bugly上报问题:_WebThreadLock() 问题

在这里插入代码片

问题分析:是没有在主线程中刷新WebView reloadData

最近的專案中,需要在UITableView中放UIWebView,webview本身會有webthreadlock,因為他在reload之後是UI的re-render,會被要求一定要在mainthread上面跑。UI繪製一定要在mainthread上[webViewreload];但是當webview要reload時,tableview也正在reload,webviewreload所使用的WebThreadLock就會被推到secondarythread,就會導致crash而

最近的專案中,需要在UITableView中放UIWebView,web view 本身會有 web thread lock,因為他在 reload 之後是 UI 的 re-render,會被要求一定要在 main thread 上面跑。
UI 繪製一定要在 main thread 上
[webView reload];
但是當 web view 要 reload 時, table view 也正在 reload,web view reload 所使用的WebThreadLock就會被推到 secondary thread ,就會導致 crash 而跳出以下的錯誤訊息:
bool _WebTryThreadLock(bool), 0x115504830: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now…1 0x108412aa8 WebThreadLock2 0x102579e77 -[UIWebView reload]
解法
強制讓[webView reload]一定要在 main thread 即可解決, call methodperformSelectorOnMainThread:withObject:waitUntilDone:指定要在 main thread 上執行的 method:
[webView performSelectorOnMainThread:@selector(reload) withObject:nil waitUntilDone:NO];
以上是在 iOS 中避免 WebThreadLock 造成 Crash的内容,更多 的内容,请您使用右上方搜索功能获取相关信息。

  1. xcode日志 只有_WebThreadLock 没有具体的错误原因,也没有定位到崩溃代码位置。crashlytics上有一下runloop相关信息
1  WebCore                        0x18d58b858 WebRunLoopLock(__CFRunLoopObserver*, unsigned long, void*) + 44

所以我猜测是不是项目中使用runloop出现了问题。全局查找,注册了所有的runloop,然后在测试 右滑关闭outlook界面,崩溃不出现了。注册代码

_timerLoop = [NSTimer timerWithTimeInterval:_durationTime target:self selector:@selector(timerScrollImage) userInfo:nil repeats:YES];[[NSRunLoop currentRunLoop] addTimer:_timerLoop forMode:NSDefaultRunLoopMode];//注册一下代码//  [[NSRunLoop currentRunLoop] runMode:UITrackingRunLoopMode beforeDate:[NSDate date]];

2. delegate问题

2. 1 webview delegate未清空导致闪退

1、报错信息:

Cannot form weak reference to instance (0x7f932cb05fb0) of class FWebViewController. It is possible that this object was over-released, or is in the process of deallocation.

2、问题描述:

A控制器(包含scrollView及其子类,并设置了其代理),B控制器(WKWebView,并设置了webView.scrollView.delegate = self),当A push 到 B,然后B pop回 A时,iOS9及其之前的版本会引起程序崩溃,iOS10 及其以后不会出现这样的问题。

3、解决办法:

根据报错信息来看:这个对象有可能是被过度释放,或正在被释放的过程中。
解决方案就是在B将要退出时把webView.scrollView.delegate = nil
在dealloc中清空代理,手动释放delegate。

4、疑问?

webView.scrollView.delegate应该和之前的A控制器的scrollview的delegate是没关系的,为什么导致崩溃的原因中感觉是有关系的?希望大神可以解释一下。

5、知识扩展:

delegate可以在dealloc里面置为nil,这是一个保险的做法,但是又不是必须的。这样做的原因是可以避免这个类被释放掉了,但是delegate却还会被引用造成的问题。

iOS项目中 bugly Crash问题汇总

绝大部分问题是对可能为nil的可选类型进行强制解包, 导致crash.其中一个crash频次较高的问题, 初步定位unity运行过程出现问题导致, 其中unityViewController被释放,
凡是引用到这个vc的地方强制解包跳转都会crash.

1. 与unity相关的crash

因为 Mono 在 iOS/Android 等移动设备上使用了 AOT 这种机制,可以针对特定平台编译成在平台优化的字节码,在资源比较紧缺的移动平台上还是有着明显优势的。而使用 AOT 编译就需要为 Trampolines 这些小东西留足足够的空间,硬编码的某个常数,在整个程序加载成功运行之后,该常数就成为了 Trampolines 运行时的配置。AOT 默认编译时给 Trampolines 的参数有点低:
nrgctx-trampolines 默认为 1024 留给递归泛型使用的空间
nimt-trampolines 默认为 128 留给接口使用的空间
ntrampolines 默认为 1024 留给泛型方法调用使用的空间
这对于小一些的项目可能是够用的,因为整体项目的结构不会太复杂,使用到的接口、泛型、递归相对也不会太多,但是对于一个稍大一些的项目来说,特别是采用了某些设计良好的第三方库的项目来说,这就比较纠结了。

目前先把这几个参数调大, 继续观察线上crash情况.
nrgctx-trampolines = 2048
nimt-trampolines = 256
ntrampolines= 2048

Mono Runtime 的官方文档中关于 trampoline 的描述:
Trampolines are small, hand-written pieces of assembly code used to perform various tasks in the mono runtime. They are generated at runtime using the native code generation macros used by the JIT. They usually have a corresponding C function they can fall back to if they need to perform a more complicated task. They can be viewed as ways to pass control from JITted code back to the runtime.
翻译如下:
Trampoline 是一些手写的非常短小的用来在 mono 运行时中执行很多操作的组件代码。主要是通过 JIT 使用到的本地代码宏在运行时动态生成的。它们通常都有与之相对应的 C 方法,在某些较为复杂的场景中,当 trampoline 无法胜任时,mono 运行时就会将这些复杂的操作交回给这些对应的 C 方法来执行。这也可以看作是将 JIT 代码的执行权交回给 runtime 的一种方式。

2. 比较隐蔽的Crash-----内存暴涨,最后被系统杀死

问题分析:

项目中进入DIY 列表页面,每次进入退出都打印Controller已经被销毁,但是内存还是莫名奇妙的增加,分析代码没有看到循坏引用问题。最后定位问题是加载图片导致的

问题代码分析:

注释掉的代码即为导致内存暴涨的根源,由于cell加载每次都将UIImage加入内存,cell销毁UIImage也没有释放。

3. 插入数据库失败,读出模型为空,强解包导致Crash

4. 循坏引用问题

5. 蓝牙命令数组下标越界,导致crash

6. UI跳转页面强解包导致Crash

7. Bool类型强解包导致Crash

8. 分母为0,导致Crash

9. 音频播放结束,强解包,导致Crash

10. 二维码扫描页面,对象已经释放,强解包,导致Crash

11. Pop页面时对象已经释放,强解包,导致Crash

12. UI对象已经释放,强解包,导致Crash

iOS crash 问题分析汇总相关推荐

  1. iOS Crash日志分析必备:符号化系统库方法

    如果你有过分析iOS崩溃日志的经验,一定经常看到日志里出现很多<redacted>的字段.这篇文章就是帮助开发者将这些字段符号化为对应的系统库方法名. 如果你已经掌握了这方面的知识,就直接 ...

  2. iOS Crash报告分析

    文章目录 相关概念 什么是 dSYM 文件 dSYM 文件有什么作用 分析crash报告 解析工具 umcrashtool 相关概念 什么是 dSYM 文件 Xcode编译项目后,我们会看到一个同名的 ...

  3. iOS crash日志分析

    项目集成talkingdata收集到的crash日志, 看到那些日志时自己也是很崩溃, 全是内存地址, 根本搞不懂项目到底crash到了那里, 比如这样: 自己在网上找了很多方法, 以下是自己最后所用 ...

  4. 了解和分析iOS Crash

    作者:孟嵩,腾讯高级开发工程师  商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. 原文链接:wetest.qq.com/lab/view/40- WeTest 导读 北京时间凌晨一点, ...

  5. 了解和分析iOS Crash Report

    翻译自苹果官方文档:Understanding and Analyzing Application Crash Reports nimo: 这篇长达1w多字的文章,大概前后翻译了一个月,"写 ...

  6. iOS Crash崩溃异常教你查看和分析

    要学会看crash崩溃和报告 一个应用程序并不总会一直运行的很好,它总会有出现crash崩溃的情况.如果在应用程序中接入了一些第三方的crash收集工具或者自建crash收集报告平台的话将会很好的帮助 ...

  7. 降本增效工具系列(二):IOS 系统 Crash 日志分析实战

    文章目录 一.前言 二.Crash 崩溃报告分析实战 三.常见 Exception Type 四.拓展阅读 一.前言 当应用程序在IOS 设备上崩溃(例如,闪退)时,一份"Crash崩溃报告 ...

  8. iOS奔溃分析技巧-crash日志符号化---symbolicatecrash

    iOS 使用symbolicatecrash分析crash文件 iOS 使用symbolicatecrash分析crash文件 首先,在桌面建一个文件夹crash,接着需要准备4个文件: 1.崩溃日志 ...

  9. iOS实录15:浅谈iOS Crash

    导语:在当前的iOS开发中,虽然ARC为开发者解决了手动内存管理时代 的许多麻烦,但是内存方面的问题依然是产生iOS Crash的元凶之一,本文介绍内存方面,有关僵尸对象.野指针.内存泄漏.废弃内存这 ...

最新文章

  1. python 聚类_使用python+sklearn实现聚类性能评估中随机分配对聚类度量值的影响
  2. 玩转spring boot——结合阿里云持续交付
  3. Coding之路——重新学习C++(2):static的详细理解
  4. JSP JSTL标签库基本使用
  5. qt无法找到动态链接库文件怎么办?
  6. python2中如何得到一级域名
  7. 百练4148:生理周期
  8. android mysql开发工具_Android开发工具--adb的使用
  9. MySQL快速上手[学习笔记](二)
  10. 递归创建多级文件目录(PHP)
  11. 初级程序员和二级Java哪个难,软考初级程序员和计算机二级哪个更受欢迎
  12. 在线IDE 在线编辑器
  13. android studio怎么创建布局,Android Studio--活动创建简单布局
  14. 【openlayers】ol3地图组成部分
  15. Windows自启动方式完全总结
  16. solr(4)solr7.3.1 使用DIH上传结构化数据
  17. 【Kalman】卡尔曼滤波基础知识
  18. Python - 定时自动获取 Bing 首页壁纸
  19. vue----前端精选50道面试题---一个小白的孤芳自赏
  20. Elixir-Pattern matching

热门文章

  1. excel怎么启用宏_如何用Excel制作一个高逼格的项目管理模板
  2. ISIS(中央系统到中央系统)
  3. 伟文标记android版本,【伟文品胜 标签打印机使用总结】APP|连接|打印|更换标签纸|优点_摘要频道_什么值得买...
  4. 边玩游戏边学python_三角函数sin,cos,tan各等于什么边比什么边
  5. Mac技巧:如何快速输入常用符号
  6. pypi, pypi-server的使用【持续更新】
  7. 【PHP】PHP7 下安装并使用 xhprof 性能分析工具
  8. vue项目 乐橙云(imouplayer.js)--轻应用直播SDK demo案例(保姆级)
  9. ERROR metastore.ObjectStore: Version information found in metastore differs 3.1.2 from expected sche
  10. 凯利讯半导体讲解金属膜电阻与碳膜电阻能不能互换?