野指针
当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称野指针
野指针异常堪称crash界
的半壁江山,相比起NSException
而言,野指针有这么两个特点:
随机性强
尽管大公司已经有各种单元、行为、自动化以及人工化测试,尽量的去模拟用户的使用场景,但野指针异常总是能巧妙的避开测试,在线上大发神威。原因绝不仅仅在于测试无法覆盖所有的使用场景
造成野指针是多样化的:首先内存被释放后不代表
内存会立刻被覆写
或者数据受到破坏
,这时候访问这块内存也不一定会出错。其次,多线程技术
带来了复杂的应用运行环境,在这个环境下,未加保护的数据可能是致命的。此外,设计不够严谨的代码
同样也是造成野指针异常的重要原因之一难以定位
NSException
是高抽象层级上的封装,这意味着它可以提供更多的错误信息给我们参考。而野指针几乎出自于C语言
层面,往往我们能获得的只有系统栈信息,单单是定位错误代码位置已经很难了,更不要说去重现修复
定位
解决野指针最大的难点在于定位。通常线上出现了crash
需要修复时,开发者最重要的一个步骤是重现crash
。而上文提到了野指针的两个特性会阻碍我们定位问题,对于这两个特性,确实也能做一些对应的处理来降低它们的干扰性:
采集辅助信息
辅助信息包括设备信息、用户行为等信息,往往可以用来重现问题。比如用户行为可以形成
用户使用路径
,从而重现用户使用场景。而在发生crash
时,采集当前页面信息,配合用户使用路径
可以快速的定位到问题发生的大概位置。经过验证,辅助信息
确实有效的减少了系统栈
对于问题重现的干扰提高野指针崩溃率
由于野指针不一定会发生崩溃这一特性,即便我们通过
堆栈信息
和辅助信息
确定了大致范围,不代表我们能顺利的重现crash
。一个优秀的野指针崩溃可以造成一天开发,三天debug
,假如野指针的崩溃不是随机的,那么问题就简单的多Xcode
提供了Malloc Scribble
对已释放内存进行数据填充,从而保证野指针访问是必然崩溃的。另外,Bugly
借鉴这一原理,通过修改free
函数,对已释放对象进行非法数据填充,也有效的提高了野指针的崩溃率Zombie Objects
Zombie Objects
是一种完全不同的野指针调试机制,将释放的对象标记为Zombie
对象,再次给Zombie
对象发送消息时,发生crash
并且输出相关的调用信息。这套机制同时定位了发生crash
的类对象以及有相对清晰的调用栈
解决方案
整理一下上述的内容,可以看到目前存在辅助信息+