iOS设备上的应用闪退时,操作系统会生成一个崩溃报告,也叫崩溃日志,保存在设备上。
1、Process Information(进程信息):
Incident Identifier: 68F2D36F-CE19-4EA3-BD3D-AE4E1C6BD0D9
CrashReporter Key: 2e7155ae1d2136fd11ed342862eccf0b3d732dc2
Hardware Model: iPhone8,1
Process: newbaichuanzhongyun [12631]
Path: /private/var/mobile/Containers/Bundle/Application/4D97BE8F-0A36-47C2-81E0-BB03F4AEC322/newbaichuanzhongyun.app/newbaichuanzhongyun
Identifier: com.baichuanzhongyun.newmeimeiyichu
Version: 1 (1.0)
Code Type: ARM-64 (Native)
Parent Process: launchd [1]
Incident Idnetifier | 崩溃报告的唯一标识符,不同的Crash |
CrashReporter Key | 设备标识相对应的唯一键值(并非真正的设备的UDID,苹果为了保护用户隐私iOS6以后已经无法获取)。通常同一个设备上同一版本的App发生Crash时,该值都是一样的。 |
Hardware Model | 代表发生Crash的设备类型,上图中的“iPad4,4”代表iPad Air |
Process | 代表Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的ID |
Path | 可执行程序在手机上的存储位置,注意路径时到XXX.app/XXX,XXX.app其实是作为一个Bundle的,真正的可执行文件其实是Bundle里面的XXX,感兴趣的可以自己查一下相关资料,有机会我后面也会介绍到 |
Identifier | 你的App的Indentifier,通常为“com.xxx.yyy”,xxx代表你们公司的域名,yyy代表某一个App |
Version | 当前App的版本号,由Info.plist中的两个字段组成,CFBundleShortVersionString and CFBundleVersion |
Code Type | 当前App的CPU架构 |
Parent Process | 当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都是launchd |
Date/Time: 2016-03-28 18:24:37.37 +0800
Launch Time: 2016-03-28 18:24:28.28 +0800
OS Version: iOS 9.2.1 (13D15)
Report Version: 105
Date/Time | Crash发生的时间,可读的字符串 |
OS Version | 系统版本,()内的数字代表的时Bulid号 |
Report Version | Crash日志的格式,目前基本上都是104,不同的version里面包含的字段可能有不同 |
3、Exception(非常重要)
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
Exception Type | 异常类型 |
Exception Subtype: | 异常子类型 |
Crashed Thread | 发生异常的线程号 |
通过崩溃的类型,你可以分析你可以具体确认是什么样的问题,基本能定位到大致的问题发生的位置
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x0000000180c88140 0x180c6c000 + 115008
1 libsystem_pthread.dylib 0x0000000180d50ef8 0x180d4c000 + 20216
2 libsystem_c.dylib 0x0000000180bf9dac 0x180b98000 + 400812
3 libc++abi.dylib 0x000000018072d3f4 0x18072c000 + 5108
4 libc++abi.dylib 0x0000000180749e98 0x18072c000 + 122520
5 libobjc.A.dylib 0x0000000180754248 0x18074c000 + 33352
6 libc++abi.dylib 0x0000000180746f44 0x18072c000 + 110404
7 libc++abi.dylib 0x0000000180746b10 0x18072c000 + 109328
8 libobjc.A.dylib 0x0000000180754120 0x18074c000 + 33056
9 CoreFoundation 0x0000000180fc9728 0x180fc0000 + 38696
10 GraphicsServices 0x00000001824d8088 0x1824cc000 + 49288
11 UIKit 0x0000000185e40d90 0x185dc4000 + 511376
12 newbaichuanzhongyun 0x000000010012b100 0x100094000 + 618752
13 libdyld.dylib 0x0000000180b6a8b8 0x180b68000 + 10424
发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。
1、Watchdog timeout
Exception Code:0x8badf00d, 不太直观,可以读成“eat bad food”,意思是don‘t block main thread
紧接着下面会有一段描述:
Application Specific Information:
com.xxx.yyy failed to resume in time
对于此类Crash,我们应该去审视自己App初始化时做的事情是否正确,是否在主线程请求了网络,或者其他耗时的事情卡住了正常初始化流程。
通常系统允许一个App从启动到可以相应用户事件的时间最多为5S,如果超过了5S,App就会被系统终止掉。在Launch,resume,suspend,quit时都会有相应的时间要求。在Highlight Thread里面我们可以看到被终止时调用到的位置,xxxAppDelegate加上行号。
PS. 在连接Xcode调试时为了便于调试,系统会暂时禁用掉Watchdog,所以此类问题的发现需要使用正常的启动模式
2、User force-quit
Exception Codes: 0xdeadfa11, deadfall
这个强制退出跟我们平时所说的kill掉后台任务操作还不太一样,通常在程序bug造成系统无法响应时可以采用长按电源键,当屏幕出现关机确认画面时按下Home键即可关闭当前程序。
跟一般的Crash结构不太一样,通常有Free pages,Wired Pages,Purgeable pages,largest process 组成,同事会列出当前时刻系统运行所有进程的信息。
关于Memory warning可以参看我之前写的一篇文章IOS 内存警告 Memory warning level。
App在运行过程中,系统内存紧张时通常会先发警告,同时把后台挂起的程序终止掉,最终如果还是内存不够的话就会终止掉当前前台的进程。
当接受到内存警告的事后,我们应该释放尽可能多的内存,Crash其实也可以看做是对App的一种保护。
4、Crash due to bugs
因为程序bug导致的Crash通常千奇百怪,很难一概而论。大部分情况通过Crash日志就可以定位出问题,当然也不排除部分疑难杂症看半天都不值问题出在哪儿。这个就只能看功底了,一点点找,总是能发现蛛丝马迹。是在看不出来时还可以求助于Google大神,总有人遇到和你一样的Bug
三、常见的Exception Type
1、Exception Type
1)EXC_BAD_ACCESS
此类型的Excpetion是我们最长碰到的Crash,通常用于访问了不改访问的内存导致。一般EXC_BAD_ACCESS后面的"()"还会带有补充信息。
SIGSEGV: 通常由于重复释放对象导致,这种类型在切换了ARC以后应该已经很少见到了。
SIGABRT: 收到Abort信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。
SEGV:(Segmentation Violation),代表无效内存地址,比如空指针,未初始化指针,栈溢出等;
SIGBUS:总线错误,与 SIGSEGV 不同的是,SIGSEGV 访问的是无效地址,而 SIGBUS 访问的是有效地址,但总线访问异常(如地址对齐问题)
SIGILL:尝试执行非法的指令,可能不被识别或者没有权限
2)EXC_BAD_INSTRUCTION
此类异常通常由于线程执行非法指令导致
3)EXC_ARITHMETIC
除零错误会抛出此类异常