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

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

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

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFzb25ibG9n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

參考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 **,能够參考objc-class.m源文件。
  • 推断是否在继承体系中——到这里,称之为Messaging过程。
  • 假设实在找不到。就运行Dynamic Method Resolution过程,即尝试调用resolveInstanceMethod:或resolveClassMethod:方法,我们能够通过实现这两个方法来动态加入方法;
  • 动态方法解析过程假设返回NO,那么还有最后的解救机会,就是Message Forwarding消息转发过程(參考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 = "<null>";
msgSendTime = 1402909302;
msgType = 12;
uuid = 0;
}

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

利用objc的runtime来定位次线程中unrecognized selector sent to instance的问题的更多相关文章

  1. UNRECOGNIZED SELECTOR SENT TO INSTANCE 问题快速定位的方法

    开发中常见的一类崩溃错误是遇到:unrecognized selector sent to instance 0xaxxxx…而backtrace又无法明确说明错误在哪行代码,如何快速定位BUG呢? ...

  2. 【Android】18.1 利用安卓内置的定位服务实现位置跟踪

    分类:C#.Android.VS2015: 创建日期:2016-03-04 一.安卓内置的定位服务简介 通常将各种不同的定位技术称为位置服务或定位服务.这种服务是通过电信运营商的无线电通信网络(如GS ...

  3. ObjC之RunTime(上)

    转载自这里. 最近看了一本书——iOS6 programming Pushing the Limits(亚马逊有中文版),最后一章是关于Deep ObjC的,主要内容是ObjC的runtime.虽然之 ...

  4. jstack可以定位到线程堆栈

    java命令--jstack 工具 JVM调优之jstack找出最耗cpu的线程并定位代码 jstack可以定位到线程堆栈,根据堆栈信息我们

  5. Handler详解系列(四)——利用Handler在主线程与子线程之间互发消息,handler详解

    MainActivity如下: package cc.c; import android.app.Activity; import android.os.Bundle; import android. ...

  6. Android利用Looper在子线程中改变UI

    MainActivity如下: package cn.testlooper; import android.app.Activity; import android.os.Bundle; import ...

  7. Handler具体解释系列(四)——利用Handler在主线程与子线程之间互发消息

    MainActivity例如以下: package cc.c; import android.app.Activity; import android.os.Bundle; import androi ...

  8. C#中的线程(中)-线程同步

    1.同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具:                       简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程 ...

  9. Erlang运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...

随机推荐

  1. Maven 实现Struts2注解配置步骤详解

    1,pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www ...

  2. (1)前言——(10)jquery项目的历史(History of the jQuery project)

    This book covers the functionality and syntax of jQuery 1.6.x, the latest version at the time of wri ...

  3. linux编程掌握常用命令

    1)编译应用程序 make -f makefile_5_2 clean make -f makefile_5_2 2)关于共享目录 在linux虚拟机的/mnt/hgfs下可看到该文件夹 3)cd命令 ...

  4. Effective C++_笔记_条款01_视C++为一个语言联邦

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) C++的各种能力和特性使它成为一个无可匹敌的工具,但也可能引发某 ...

  5. 14.8.2 Verifying File Format Compatibility 校验文件格式兼容性:

    14.8.2 Verifying File Format Compatibility 校验文件格式兼容性: 14.8.2.1 Compatibility Check When InnoDB Is St ...

  6. uva 129

    暴力求解 大致题意 如果一个字符串含有相邻的重复字串称为容易的串,反之为非容易 求字典序第n困难的串…… 大致思路,暴力如果是容易的串停过,然后困难的串继续求解tot++ 总之先记着吧…… 最后输出格 ...

  7. Linux经常使用命令(十一) - more

    more命令,功能类似 cat ,cat命令是整个文件的内容从上到下显示在屏幕上. more会以一页一页的显示方便使用者逐页阅读,而最主要的指令就是按空白键(space)就往下一页显示,按 b 键就会 ...

  8. Qml 写的弹出层控件(13篇博客)

    QML弹出窗口组件,灯箱效果.动画效果,可拖拽 核心思路:一个mask层,一个最顶层,都用rectangle,禁止事件穿透 使用 Popup { id: popup width: 200; heigh ...

  9. mysql sql执行顺序

    <pre name="code" class="html">mysql> explain select * from (select * fr ...

  10. c++ try throw catch

    c++ try throw catch 这三者联合使用 , try { statement list; } catch( typeA arg ) { statement list; } catch( ...