1. 瓦解
多指在移动装备(如iOS、Android装备)中或不可移动装备(如:Windows、Linux等装备),
在打开或使用应用步伐时出现的突然退出停止的环境(类似于Windows的应用步伐瓦解)。
多体现为:应用步伐画面一闪而过,随即退回到桌面。
瓦解会影响用户体验,造成用户流失,因此,我们要重视瓦解
根据差别场景,瓦解网络方式差别
Xcode编译期间:
测试机获取:
Xcode->Window->Devices and Simulators
或者
设置->隐私->分析与改进
线上瓦解采集:
封装好的三方,直接接入的:友盟、bugly、听云
开源的SDK:KSCrash、plcrashreporter
2. 瓦解产生的原因
分别方式一:
2.1 cpu无法执行的代码
- 无效指令或操纵
- 访问无效地址及不具有权限的内存地址
- 除以0等
- 僵尸对象
…
2.2 被系统强杀
- 应用内存斲丧过高,即OOM标题
- 主线程长时间无法响应ANR
- 资源非常
- 死锁
- 非法的应用署名
- 后台执行超时
…
2.3 语言触发非常
分别方式二:
除了上面的分别方式,瓦解还可以按:软件非常、硬件非常分别
软件非常:
软件非常重要来自 kill(),pthread_kill()。iOS 中的 NSException 未捕捉,abort 都属于这种环境。
硬件非常:
硬件的信号始于处理器 trap,是和平台相关的。野指针瓦解大部分是硬件非常。
软件非常的流程是:
软件非常 -> Unix信号
硬件非常的流程是:
硬件非常 -> Mach非常 -> Unix信号
Mach 非常:
- EXC_BAD_ACCESS: 不能访问的内存
- EXC_BAD_INSTRUCTION: 非法或未定义的指令或操纵数
- EXC_ARITHMETIC: 算术非常(例如除以0)。iOS 默认是不启用的,所以我们一般不会遇到
- EXC_EMULATION: 执行打算用于支持仿真的指令
- EXC_SOFTWARE:软件天生的非常,我们在 Crash 日志中一般不会看到这个范例,苹果的日志里会是 EXC_CRASH
- EXC_BREAKPOINT:跟踪或断点
- EXC_SYSCALL: UNIX 系统调用
- EXC_MACH_SYSCALL: Mach 系统调用
UNIX 信号:
- SIGSEGV,段错误。访问未分配内存、写入没有写权限的内存等。
- SIGBUS,总线错误。好比内存地址对齐、错误的内存范例访问等。
- SIGILL,执行了非法指令,一般是可执行文件出现了错误。
- SIGFPE ,致命的算术运算。好比数值溢出、NaN数值等。
- SIGABRT,调用 abort() 产生,通过 pthread_kill() 发送。
- SIGPIPE,管道破裂。通常在进程间通信产生。好比接纳FIFO(管道)通信的两个进程,读管道* 没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。根据苹果相关文档,可以忽略这个信号。
- SIGSYS,系统调用非常。
- SIGKILL,此信号表现系统中止进程。瓦解陈诉会包罗代表中止原因的编码。exit(), kill(9) 等函数调用。iOS 系统杀进程,如 watchDog 杀进程。
- SIGTRAP,断点指令或者其他trap指令产生。
当我们拦截信号处理之后是可以让步伐不瓦解而继承运行的,但是不发起如许做,由于步伐已经处于非常不可知状态。
看上图内里最终都会转换为 “UNIX信号”, 是不是代表我们只用监听 “UNIX 信号” 就够了呢?为什么还要拦截 Mach 非常呢?
不是所有的 "Mach非常” 范例都映射到了 “UNIX信号”
“UNIX信号” 在瓦解线程回调,如果遇到 Stackoverflow 标题,已经没有条件(栈空间)再执行回调代码了。
捕捉到非常信号后,在处理方法 handleSignalException 里通过 backtrace_symbols 方法就能获取到当前的堆栈信息。
堆栈信息可以先生存在本地,下次启动时再上传到瓦解监控服务器就可以了。
Mach是一个XNU的微内核核心
3. 瓦解日志包罗信息
根本信息:瓦解发生的日期、iOS 版本等
进程信息:瓦解进程的相关信息,好比瓦解陈诉唯一标识符、唯一键值、装备标识
非常信息:非常范例、非常编码、非常的线程;
线程回溯:瓦解时的方法调用栈
瓦解日志调用栈内容:
如许直接看瓦解信息的调用栈,是看不懂的
我们需要一个.DSYM文件,dSYM 是生存 函数地址映射信息的中转文件,其中包罗文件名、方法名、行号等信息,是和可执行文件的16进制函数地址一一对应的,通过分析瓦解的瓦解文件可以准确知道详细的瓦解信息。
一个ipa包,对应一个.DSYM文件
每次编译天生的 dSYM都会有所差别,通常 dSYM 中会有一个唯一标识,称作 UUID ,用以区分差别的 dSYM 文件。
UUID(Universally Unique Identifier):一个app包对应一个UUID
UDID(Unique Device Identifier):一个手机装备对应一个UDID
通过.DSYM文件的解析,就可以找到对应的文件名和函数名
详细的做法?
方法一:
详细的做法:
- 首先要通过瓦解信息内里,找到UUID,然后通过UUID找到对应精确的.dsym的
- 找到瓦解日志的关键信息,获取:偏移量、运行时堆栈地址、运行时APP起始地址
相对于起始地址的偏移量为 29796,运行时堆栈地址为 0x102a47464,运行时APP起始地址为 0x102a40000。
- 通过.dsym拿到起始地址(dSYM 文件中生存中符号表 TEXT 段的起始地址),再加上瓦解日志的偏移量,获取运行时堆栈地址在.dsym的真正地址
- 查找.dsym上的运行地址,解析即可
由于 iOS 加载 Mach-O 文件时为了安全使用了 ASLR(Address Space Layout Randomization) 机制,导致二进制 Mach-O 文件每次加载到内存的首地址都会不一样,但是偏移量,加载地址,起始地址的盘算规则是一样的;
从上面我们可以得到 0x102a47464 (运行时地址) = 0x102a40000 (起始地址) + 29796(偏移量)这个公式。
因此通过 dSYM 的起始地址和偏移量就可以盘算出 0x102a47464 对应在 dSYM 中的地址为 0x100007464 = 0x0000000100000000 + 29296。
OS 瓦解日志在线符号化实践
方法二:使用下令atos
跟前面那个方法的理论是同等的,只是下令不一样
- atos -o 项目名.app.dSYM/Contents/Resources/DWARF/项目名 -l 基地址(运行初始地址) 偏移后的地址
- 笔者使用的命令参考如下
- atos -o Test.app.dSYM/Contents/Resources/DWARF/Test -l 0x1026b4000 0x1026B9904
复制代码 iOS dSYM详解和分析crash,ips文件
方法三:使用symbolicatecrash脚本
symbolicate:象征
symbolicatecrash是一个Perl脚本,随Xcode提供,专门用于符号化瓦解日志。你需要找到这个脚本的位置(通常在Xcode的App包内),然后在下令行中使用它,如下:
使用下令find /Applications/Xcode.app -name symbolicatecrash -type f获取到symbolicatecrash
/path/to/symbolicatecrash /path/to/MyApp.crash /path/to/MyApp.dSYM > Symbolicated.crash
在桌面上新建一个文件夹,把.app文件、.app.dSYM文件、.crash文件、symbolicatecrash工具,一起放到新建的文件夹下:
然后执行下令:
- export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
- ./symbolicatecrash JCrashDemo.crash JCrashDemo.app.dSYM > new.crash
复制代码 即可获取一个新的解析好的crash文件
使用symbolicatecrash解析瓦解日志
方法四:使用其他更便捷的脚本
即,将好几步要执行的步调,简单化
4. 开发中常见瓦解
- unrecognized selector crash
- KVO crash
- NSNotification crash
- NSTimer crash
- Container crash(数组越界,插nil等)
- NSString crash (字符串操纵的crash)
- UI not on Main Thread Crash (非主线程刷UI(机制待改善))
- …
unrecognized selector crash
当一个对象找不到对应的方法实现的时候,会报此类错误
在找不到方法时,查找方法将会进入方法forward流程,系统给了三次调停的机会,
所以我们要办理这个标题,在这三次均可以办理这个标题
KVO crash
KVO监听对象属性值的改变
KVO 一样平常使用造成瓦解的原因通常有以下几个:
- KVO 添加次数和移除次数不匹配:
- 移除了未注册的观察者,导致瓦解。
- 重复移除多次,移除次数多于添加次数,导致瓦解。
- 重复添加多次,虽然不会瓦解,但是发生改变时,也同时会被观察多次。
- 被观察者提前被释放,被观察者在 dealloc 时仍然注册着 KVO,导致瓦解。
- …
NSNotification crash
当一个对象添加了notification之后,如果dealloc的时候,仍然持有notification,就会出现NSNotification范例的crash。
在iOS9以及iOS9以后,可以不做移除关照操纵
NSTimer crash
一般是在定时器被target强引用没有被释放,产生内存泄漏,或者在定时器触发的时候导致瓦解
办理方案:
1.NSTimer使用Block,对其target不强引用
2.是否找到一个合适的时机,在确定NSTimer已经失效的环境下,让NSTimer自动invalidate
3.使用中心变量
类族(NSArray,NSMutableArray,NSDictonary,NSMutableDictionary)
发起:
- 加三目运算符,防止数据为空
- 数组取值的时候,判定数组个数
- 遇到可变范例,使用strong修饰
非主线程刷UI
UI刷新,必须在主线程操纵
在子线程中,需要刷新UI的时候,要切换到主线程
野指针
定义:当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍然指向已经回收的内存地址,此环境下该指针便称野指针
若操纵系统将这部分已经释放的内存重新分配给另外一个进程,而原来的步伐重新引用如今的失路指针,则将产生无法预料的结果。由于此时失路指针所指向的内存如今包罗的已经完满是差别的数据。
办理方法:
Xcode开启僵尸对象监听
使用MLeaksFinder或其他三方库,排查内存泄漏标题
使用JJException,淘汰App闪退
固然,最好做到:开发过程不使用,正式包开启
参考资料:
iOS 野指针处理
iOS野指针定位总结
Chat-GPT有关野指针的解释
问:iOS野指针错误一般由什么引起的?怎么排查与避免?
答:
iOS 开发中的野指针错误通常是指向一个已经被释放的对象的指针仍然被访问时发生的错误。这种错误可能导致应用瓦解或不稳固的行为。野指针错误的重要原因包括但不限于:
- 对象被提前释放:当你实验访问一个已经被释放的对象时,就会出现野指针错误。这通常是由于对象的引用计数被错误管理,如被提前释放或没有精确地持有对象。
- 内存管理不当:在手动管理内存(如使用MRC, 手动引用计数)的项目中,如果没有精确地平衡retain和release调用,可能会导致对象提前释放,从而引发野指针错误。
- 指针未初始化或重置:使用未初始化的指针或者在释放对象后未将指针设为nil,也可能导致野指针访问。
如何排查野指针错误:
- 使用Xcode的调试工具:利用Xcode的调试工具,如LLDB调试器,可以资助你定位野指针的来源。Zombies工具尤其有用,它可以资助追踪已释放对象的访问实验。
- 代码审查:定期举行代码审查可以资助发现潜在的内存管理错误,特别是对于使用MRC的项目。
- 静态分析工具:Xcode自带的静态分析工具可以资助检测潜在的内存管理标题,包括可能导致野指针错误的环境。
如何避免野指针错误:
- 使用自动引用计数(ARC):自动引用计数可以大大淘汰内存管理错误。确保项目启用ARC,让编译器资助管理对象的生命周期。
- 指针置nil:在释放对象后,将指向该对象的指针置为nil,如许纵然实验访问该指针,也不会导致瓦解。
- 避免使用未初始化的指针:在声明指针变量时,初始化为nil,以避免使用未初始化的指针。
- 遵循内存管理规则:纵然在ARC环境下,也需要理解并遵循根本的内存管理规则,如不要持有非自己创建的对象,除非这个对象已经明白地被retain了。
- 使用弱引用:对于不需要拥有对象的引用,使用弱引用可以避免循环引用和提前释放的标题。
通过接纳这些措施,你可以显著淘汰应用中野指针错误的发生,进步应用的稳固性和可靠性。
不得不说,回复的还蛮好的
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |