Writing Crash-Free Code

面试的时候经常会被问到“程序崩溃都有哪些原因造成”,突然想的话想不到那么全面, 总结一下以备不时之需,也可以在写代码时注意下。另外,“如何处理崩溃”也是面试官喜欢问的问题,也做个总结。

程序崩溃都有哪些原因造成

  • 可变数组的操作
    1. 使用 (for in) 或迭代器遍历可变数组时删除数组元素,改为逆序遍历或者添加个数组副本
    2. 可变数组声明属性为 copy
      // .h 文件
      @property (nonatomic, copy) NSMutableArray mutableArray;
      // .m 文件
      NSMutableArray
      array = @[@1,@2];
      self.mutableArray = array;
      // 下句崩溃,因为 NSArray 中找不到 removeObjectAtIndex 方法
      [self.mutableArray removeObjectAtIndex:0];
  • 多线程中访问同一块资源时,没有为数据加锁,数据错误可能会导致崩溃。
  • 在多线程中,UI 等操作需要放到主线程操作,否则会导致崩溃
  • 操作野指针。
  • 数组越界,像 NSDictionary 或者 NSArray 中插入数组等
  • 调用对象不存在的方法,unrecognized selector
  • 低内存终止,看门狗结束程序,大多是内存泄漏等导致。
  • 内存泄漏详情见之前的文章

发生 EXC_BAD_ACCESS 的错误,通过全局断点进行定位。可以使用 NSZombies 可以定位野指针。资源竞争的定位可以通过 Thread Sanitizer 查找。

修复线上崩溃的方法

使用 symbolicatecrash 工具将崩溃堆栈符号化

创建新的文件夹,将 symbolicatecrash、.crash 和 .dSYM 文件放到该文件夹下执行下面命令,生成新的 symbol.crash 文件,该文件中可以显示崩溃函数名及位置。

./symbolicatecrash ./*.crash ./*.app.dSYM > symbol.crash

发生 Error: “DEVELOPER_DIR” is not defined 错误可在终端继续输入以下指令设置环境变量

export DEVELOPER_DIR=/Applications/XCode.app/Contents/Developer

以上三个文件的位置:

symbolicatecrash 可以在终端输入下面命令查找其所在位置

find /Applications/Xcode.app -name symbolicatecrash -type f

.crash 文件在 Xcode -> Window -> Organizer -> 选中 App 的 Crashes ,选中需要解决的问题,右键 -> Show in Finder,找到 .xccrashpoint 文件,右键 -> Show Package Content 后打开的目录中包含 .crash 文件

.dSYM 文件在 Xcode -> Window -> Organizer -> 选中 App 的 Archives,右侧面板中 Download dSYMs 后 .xcarchive 中包含 .dSYM 文件

用 Console 工具打开新生成的 .crash 文件,除了能看到崩溃调用堆栈外,还可以看到崩溃的机型、系统等,另外 Exception Type 会显示崩溃原因如 Exception Type: EXC_CRASH (SIGABRT)

以下列出信号类型,更多点击这里

SIGABRT – 程序中止命令中止信号 (通常指由于某个对象接收到未实现的消息引起的)
SIGALRM – 程序超时信号
SIGFPE – 程序浮点异常信号
SIGILL – 程序非法指令信号
SIGHUP – 程序终端中止信号
SIGINT – 程序键盘中断信号
SIGKILL – 程序结束接收中止信号
SIGTERM – 程序kill中止信号
SIGSTOP – 程序键盘中止信号
SIGSEGV – 程序无效内存中止信号 (一般是表示内存不合法)
SIGBUS – 程序内存字节未对齐中止信号
SIGPIPE – 程序Socket发送失败中止信号

使用 atos

atos 比较适用于当你把应用发布到多个渠道,需要对不同渠道的 crash 文件进行分析时,或者不确定哪个 dSYM 对应哪个归档文件时使用。

获取崩溃 Build 的唯一标识 UUID
如输入

xcrun dwarfdump --uuid appName.app/appName    

获取 dSYM 文件的 UUID

xcrun dwarfdump -–uuid appName.app.dSYM

如果 .crash、dSYM 以及 app 中的 UUID 都一致说明这三个文件是对应的。

接下来获取崩溃文件的崩溃函数地址

atos [-o AppName.app/AppName] [-l loadAddress] [-arch architecture]

如输入

xcrun atos -o appName.app.dSYM/Contents/Resources/DWARF/appName -l 0x1000 -arch armv7

则会打印崩溃地址指向的函数名和所在行数

使用友盟

友盟的崩溃文件下载后使用它自己的工具将崩溃文件生成 cvs 文件

使用第三方工具 BugHd

该工具可以将崩溃信息和 dSYM 结合查看,点击此处查看详情

请我喝汽水儿