博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用objc的runtime来定位次线程中unrecognized selector sent to instance的问题
阅读量:4614 次
发布时间:2019-06-09

本文共 2498 字,大约阅读时间需要 8 分钟。

昨天遇到一个仅仅有一行错误信息的问题:

-[NSNull objectForKey:]: unrecognized selector sent to instance 0x537e068

因为这个问题发生在次线程。所以没有太实用的堆栈信息。而是仅仅有简单的SIGABRT信息:

考虑到unrecognized selector sent to instance这类问题是因为向某个对象发送了未实现的消息,这个过程大致例如以下(图片摘自):

參考Objective-C的对象模型:

struct objc_class {    Class isa  OBJC_ISA_AVAILABILITY;#if !__OBJC2__    Class super_class                                        OBJC2_UNAVAILABLE;    const char *name                                         OBJC2_UNAVAILABLE;    long version                                             OBJC2_UNAVAILABLE;    long info                                                OBJC2_UNAVAILABLE;    long instance_size                                       OBJC2_UNAVAILABLE;    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;#endif} OBJC2_UNAVAILABLE;/* Use `Class` instead of `struct objc_class *` */

消息发送的流程大致例如以下:

  • 推断发送的消息是否为retain等内存管理方法。
  • 推断receiver是否为nil;
  • 推断是否在方法缓存中,即struct objc_cache *cache;
  • 推断是否在方法列表中,即struct objc_method_list **methodLists,因为对象的方法能够动态加入,所以这里的类型是struct objc_method_list **,能够參考源文件。
  • 推断是否在继承体系中——到这里,称之为过程。
  • 假设实在找不到。就运行过程,即尝试调用resolveInstanceMethod:或resolveClassMethod:方法,我们能够通过实现这两个方法来动态加入方法;
  • 动态方法解析过程假设返回NO,那么还有最后的解救机会,就是消息转发过程(參考NSObject.h):

- (id)forwardingTargetForSelector:(SEL)aSelector;- (void)forwardInvocation:(NSInvocation *)anInvocation;- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

我第一反应是加入resolveInstanceMethod:来观察,这是一个类方法。所以得加入到metaClass上:

Class metaClass = objc_getMetaClass("NSNull");SEL sel = @selector(resolveInstanceMethod:);const char *type = "c@::";class_addMethod(metaClass, sel, (IMP)resolveInstanceMethod, type);
但遗憾的是,即便此时方法寻找不到时会调用到resolveInstanceMethod:方法。只是在设置的断点位置看也已经没有明白的堆栈信息了。所以我就直接加入找不到的方法objectForKey:来定位:

Class metaClass = objc_getMetaClass("NSNull");SEL sel = @selector(objectForKey:);const char *type = "@@:@";class_addMethod(metaClass, sel, (IMP)objectForKey, type);
样一来。通过在我们加入的objectForKey方法中设置断点就能够获取到具体堆栈信息,从而进一步定位到问题所在:
{  fromId = "\U6d4b\U8bd520#\U65fa\U4f01\U65e0\U7ebf\U6d4b\U8bd5";  msgContent = "
"; msgSendTime = 1402909302; msgType = 12; uuid = 0;}

原因是因为服务端推送的消息中一个必填字段为空。而client也刚好在此处没有使用项目代码中约定的类型检查宏(此处应为VFDict),而是直接当做NSDictionary来操作。

转载于:https://www.cnblogs.com/mfrbuaa/p/5084719.html

你可能感兴趣的文章
微积分初步
查看>>
毕业论文格式范例讲解
查看>>
js的块级作用域
查看>>
委托、Lambda表达式和事件
查看>>
typecho模板制作代码收集
查看>>
Python学习笔记4:集合方法
查看>>
POJ - 3696 同余
查看>>
[随想感悟] 《归去来兮辞·并序》 赏析
查看>>
elasticsearch的监控脚本
查看>>
USACO 之 Section 1.3 Greedy Algorithm (已解决)
查看>>
数组排序
查看>>
51Nod 1090: 3个数和为0
查看>>
dos 操作显示 > nul 2>nul
查看>>
Lucene初级教程
查看>>
leetcode 205. Isomorphic Strings(哈希表)
查看>>
爬取校园新闻首页的新闻
查看>>
OpenStack学习系列-----第二篇 由一个错误看理解整个架构的重要性
查看>>
android 透明度
查看>>
安装mongodb到系统服务
查看>>
python自带的帮助文档
查看>>