1、APP运行崩溃的类型
APP 在运行中如果出现程序崩溃退出,iOS系统会生成一份日志保存在本地,日志中的Exception type: 会标识出本次崩溃的类型
1.1 EXC_BAD_ACCESS
这个类型的错误遇到的频率最高,例如:强制解包且访问对象为空时的错误类型、数组越界、解析错误。
在访问一个已经释放的对象或向它发送消息时,EXC_BAD_ACCESS就会出现。造成EXC_BAD_ACCESS最常见的原因是,在初始化方法中初始化变量时用错了所有权修饰符,这会导致对象过早地被释放。举个例子,在viewDidLoad方法中为UIViewController创建了一个包含元素的NSArray,却将该数组的所有权修饰符设成了assign而不是strong。现在在viewWillAppear中,若要访问已经释放掉的对象时,就会得到名为EXC_BAD_ACCESS的崩溃。
这个崩溃发生时,查看崩溃日志,却往往得不到有用的栈信息。还好,有一个方法用来解决这个问题:NSZombieEnabled。
这是一个环境变量,用来调试与内存相关的问题,跟踪对象的释放过程。启用了NSZombieEnabled的话,它会用一个僵尸实现来去你的默认的dealloc实现,也就是在引用计数降到0时,该僵尸实现会将该对象转换成僵尸对象。僵尸对象的作用是在你向它发送消息时,它会显示一段日志并自动跳入调试器。
所以,当在应用中启用NSZombie而不是让应用直接崩溃时,一个错误的内存访问就会变成一条无法识别的消息发送给僵尸对象。僵尸对象会显示接收到的消息,然后跳入调试器,这样你就可以查看到底哪时出了问题。
通常可以通过选中xcode中的edit scheme->dialognostics->zombie objects 来让xcode在出现崩溃的时候log出崩溃的原因。
SIGSEGV 和 SIGBUS 都是 EXC_BAD_ACCESS的子类。查看崩溃日志时一般都会看到 Exception type: EXC_BAD_ACCESS (SIGSEGV) 这样的描述。
1.2 SIGSEGV ( EXC_BAD_ACCESS的子类)
段错误信息(SIGSEGV)是操作系统产生的一个问题。当硬件出现错误、访问不可读的内存地址或向受保护的内存地址写入数据时,就会发生这个错误。
硬件错误这一情况并不常见。当要读取保存在RAM中的数据,而该位置的RAM硬件有问题时,你会收到SIGSEGV。当要读取一个指针的值,而它指向无效内存地址的值时,你也会收到SIGSEGV。
SIGSEGV错误调试起来更困难,而导致SIGSEGV的最常见原因是不正确的类型转换。要避免过度使用指针或尝试手动修改指针。在手动修改指针时没有注意内存对齐和填充问题(有待研究啥是内存对齐和填充),就会收到SIGSEGV。
1.3 SIGBUS
总线错误信号(SIGBUG)代表无效内存访问,即访问的内存是一个无效的内存地址。也就是说,那个地址指向的位置根本不是物理内存地址(它可能是某个硬件芯片的地址)。SIGSEGV和SIGBUS都属于EXC_BAD_ACCESS的子类型。
1.4 SIGTRAP
SIGTRAP代表陷阱信号。它并不是一个真正的崩溃信号。它会在处理器执行trap指令发送。LLDB调试器通常会处理此信号,并在指定的断点处停止运行。如果你收到了原因不明的SIGTRAP,先清除上次的输出(clean一下项目),然后重新进行构建通常能解决这个问题。
1.5 SIGKILL
用户强制退出APP时出现,用户权限没有开放也会导致这个问题,xcode输出为:privacy-sensitive data without a usage description。。
1.6 看门狗超时
这种崩溃通常比较容易分辨,因为错误码是固定的0x8badf00d。(程序员也有幽默的一面,他们把它读作Ate Bad Food。)在iOS上,它经常出现在执行一个同步网络调用而阻塞主线程的情况。因此,永远不要进行同步网络调用。
2、 崩溃问题分析手段
2.1、 开发中
开发过程中遇到的崩溃可以通过查看崩溃位置,查看xcode Log输出来定位问题
2.2、 测试过程中
测试过程中遇到的崩溃可以通过查看测试机上的崩溃日志来定位崩溃位置,这里有个符号化的问题,当测试机连接电脑后通过MAC的iTunes软件可以同步手机信息,然后在xcode->Device and simulator -> view device log 中可以看到手机上所有的崩溃日志,打开日志等几秒钟xcode会自动将日志符号化。
2.3、已经上线
已经上线的APP出现崩溃时需要分两种情况,一种是用户打开了数据分享功能,这样的话我们可以直接从xcode->organizer->crash中查看线上的崩溃日志。第二种情况是用户没有打开数据分享权限,这种情况下就需要用户配合获取到用户的崩溃日志,手动符号化来定位崩溃位置(符号化方法)。
另外一种比较简便的方法是在项目中集成Bugly并设置符号化,也是推荐使用过的方式。
参考文档: