利用objc的runtime来定位次线程中unrecognized selector sent to instance的问题
昨天遇到一个仅仅有一行错误信息的问题:
-[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的问题的更多相关文章
- UNRECOGNIZED SELECTOR SENT TO INSTANCE 问题快速定位的方法
开发中常见的一类崩溃错误是遇到:unrecognized selector sent to instance 0xaxxxx…而backtrace又无法明确说明错误在哪行代码,如何快速定位BUG呢? ...
- 【Android】18.1 利用安卓内置的定位服务实现位置跟踪
分类:C#.Android.VS2015: 创建日期:2016-03-04 一.安卓内置的定位服务简介 通常将各种不同的定位技术称为位置服务或定位服务.这种服务是通过电信运营商的无线电通信网络(如GS ...
- ObjC之RunTime(上)
转载自这里. 最近看了一本书——iOS6 programming Pushing the Limits(亚马逊有中文版),最后一章是关于Deep ObjC的,主要内容是ObjC的runtime.虽然之 ...
- jstack可以定位到线程堆栈
java命令--jstack 工具 JVM调优之jstack找出最耗cpu的线程并定位代码 jstack可以定位到线程堆栈,根据堆栈信息我们
- Handler详解系列(四)——利用Handler在主线程与子线程之间互发消息,handler详解
MainActivity如下: package cc.c; import android.app.Activity; import android.os.Bundle; import android. ...
- Android利用Looper在子线程中改变UI
MainActivity如下: package cn.testlooper; import android.app.Activity; import android.os.Bundle; import ...
- Handler具体解释系列(四)——利用Handler在主线程与子线程之间互发消息
MainActivity例如以下: package cc.c; import android.app.Activity; import android.os.Bundle; import androi ...
- C#中的线程(中)-线程同步
1.同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具: 简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程 ...
- Erlang运行时中的无锁队列及其在异步线程中的应用
本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...
随机推荐
- ASP.NET、WinForm、C# - 配置文件信息读取 [ Web.config || Appconfig ]
<configuration> <appSettings> <add key="name" value="HF_Ultrastrong&qu ...
- 能够返回运行结果的system函数加强版本号
/********************************************************************* * Author : Samson * Date ...
- HDU 3068 最长回文 Manacher算法
Manacher算法是个解决Palindrome问题的O(n)算法,能够说是个超级算法了,秒杀其它一切Palindrome解决方式,包含复杂的后缀数组. 网上非常多解释,最好的解析文章当然是Leetc ...
- 使用malloc分别分配2KB,6KB的内存空间,打印指针地址
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<malloc.h> i ...
- squid+apache实现缓存加速
本实例是squid和apache在同一台机器上,squid做前端反向代理.port为80,apache作为后端web,port为81 serverip:172.16.8.102 1.首先介绍下版本号选 ...
- MongoDB学习笔记(三) 在MVC模式下通过Jqgrid表格操作MongoDB数据
看到下图,是通过Jqgrid实现表格数据的基本增删查改的操作.表格数据增删改是一般企业应用系统开发的常见功能,不过不同的是这个表格数据来源是非关系型的数据库MongoDB.nosql虽然概念新颖,但是 ...
- ASP.NET - 分页
效果: SQL-存储过程(Paging): ROW_NUMBER() over(order by MessageDateTime desc) 其中的 MessageDateTime desc 代表的 ...
- CTreeCtrl结点拖动实现(与后台联动)
原帖及讨论:http://bbs.bccn.net/thread-211413-1-1.html 效果描述:鼠标点击并拖动某一结点可以把它以动到其他结点下.原理:把一个结点机器下面的所有结点在需要释放 ...
- Delphi的TService的輸入桌面切換(服务程序)(windows登录界面如何截图)(使用了OpenDesktop和GetThreadDesktop等API)
dfm: object CopyDeskService: TCopyDeskService OldCreateOrder = False OnCreate = ServiceCreate OnD ...
- csdn发博文验证码缺陷
csdn验证码的长处: 一,差点儿没有浪费人脑人力,却要花去机器人非常多cpu csdn发博文验证码却有非常大缺陷: 一.验证码的内容终于结果是简单的数字,能够穷举尽的,也就是说不怕被封号的话全然能够 ...