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

-[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. HTML+CSS+JS - 5秒钟之后跳转页面

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.c ...

  2. Mac os 进行Android开发笔记(2)

    Mac OS X 下开发 Android  1> MAC OS Android环境搭建: 2>MAC OS  配置 Andoid ADB: 3>MAC OS 上进行Android真机 ...

  3. Freemarker概念简单介绍

    Freemarker概念简单介绍 1.   Freemarker是什么 模板引擎:一种基于模板的,用来生成输出文本的通过工具. 基于java开发包和类库 2.   Freemarker能做什么 MVC ...

  4. 译文:前端性能的重要性 The Importance of Frontend Performance

    欢迎訪问我的主页.最新的文章我会首先公布在个人主页上: http://blog.guaidm.com/shocky/ 原书下载地址:http://pan.baidu.com/s/1pJocRwB 在我 ...

  5. libcurl编程,整整17篇

    http://www.cnblogs.com/lidabo/category/639633.html

  6. MySQL备份方案-->(利用mysqldump以及binlog二进制日志)

                                                         MySQL备份方案-->(利用mysqldump以及binlog二进制日志) 随着数据不 ...

  7. Swift - 给项目导入资源

    如果想添加资源到项目中去,只要通过鼠标左键将文件或者存有文件的文件夹直接拖到Xcode中.   当松开鼠标后会弹出如下面板: (1)勾上“Copy items if needed”就会拷贝文件进项目, ...

  8. 第十六周oj刷题——Problem J: 填空题:静态成员---计算学生个数

    Description 学生类声明已经给出.在主程序中依据输入信息输出实际建立的学生对象个数,以及全部学生对象的成绩总和. Input 学生个数 相应学生个数的学生信息(姓名    年龄    成绩) ...

  9. [精读]Spationtemporal Saliency Detection Using Textural Contrast and Its Applications

    Spationtemporal Saliency Detection Using Textural Contrast and Its Applications Last Edit 2013/12/3 ...

  10. MFC 用gdi绘制填充多边形区域

    MFC 用gdi绘制填充多边形区域 这里的代码是实现一个三角形的绘制,并用刷子填充颜色 在OnPaint()函数里面 运用的是给定的三角形的三个点,很多个点可以绘制多边形 CBrush br(RGB( ...