网上有一篇So you crashed in objc_msgSend()的文章广为流传,介绍了如何定位oc层常出现的内存错误,这种内存错误常常最终,引起类似 0 libobjc.A.dylib 0x37991f78 objc_msgSend + 16 的崩溃信息。
详细的方法在原文中有介绍,但经过实战演练,我发现并没有想象中那么美好。
首先正如文章中所说,oc的运行时确实是把接收消息的实例地址放在r0中,把消息方法的地址放在r1中。
由于oc特殊的方法调用的实现,oc是把方法以字符串形式存在内存中,所以通过r1中地址,可以直接取得要调用方法的可读形式。这已经很难得了,因为通过这个方法已经能把范围缩小很多。
然后对于地址存在r0中的这个方法接收的实例,在非调试状态下,我目前还没找到合适的方法可以还原出它的类型信息,我发现即使从该地址的实例被实际回收到再次调用该已被回收的地址的实例作为方法接受者只间隔一个runloop,该地址指向的内存实际内容也变化得非常快,迅速就被其他内容覆盖掉了。
实际上对于一个实例,我发现一般从这个实例所在地址的第一个字节开始的4个字节的内容记录了一个32位的地址,该地址指向该实例所属于的类型的地址。但由于该值很快被新的数据覆盖,所以我也没有办法取得野指针实例的类型,也就不能进一步缩小问题的范围。这个问题有一个解决思路,但只在调试状态有用,那就是记录这个地址的内存申请情况,gdb应该有类似的调试命令,记录了类似的信息,lldb貌似只有在osx下有效。