(字节面试题)
两种方法:
-
Malloc Scribble
-
Xcode 的僵尸模式
总体的解决思路
因为野指针的 crash 是因为对象被释放了,但是指针没有修改,还指着那片内存地址,如果这是访问这个指针就会 crash。 这个事情是随机的,指针指向的内存有可能回收了,但也可能是被其他对象覆盖了(这也会导致错误),也有可能还保留着。所以定位野指针的关键在于「让 crash 必现」,必现了就能定位代码,就能改正。
Malloc Scribble
原理是 hook free() 函数,换成自定义的 new_free 函数,在 new_free() 函数中,给要释放的内存地址填充 0x55, 但是不会实质上释放这块内存。后续如果访问到这块内存,马上发生 crash。
不过有个问题,这样直接获取到的崩溃信息有限,所以还需要利用代理类(继承 NSProxy 的子类)重写 Normal Forwarding 的两个方法,以及 NSObject 的实例方法,来获取更详细的崩溃信息。
会把代理类的 isa 指针复制到这块内存中,这样,对这块内存的发送的所有消息就会跳到代理类的方法中了(为什么是所有消息?因为 NSObject 相关的方法代理类重写了,还重写了消息转发最后一个阶段的两个方法)
NSProxy 到底是什么东西?
僵尸对象
在 dealloc() 函数中,把要释放的对象替换为僵尸对象(会用 objc_destructInstance 释放这个对象),再次访问僵尸对象时会进入消息转发流程,这时会输出日志并发生 crash。
跟第一种方法的区别是:僵尸对象只适用于OC 对象,无法针对 malloc 出来的内存
参考资料
- iOS底层原理 36:内存优化(一) 野指针探测:月老师的文章,够全面
- iOS 野指针处理:配图挺好看的