unrecognized selector sent to instance XXXXX
unrecognized selector
SEL(@selector)原理:@selector()是取类方法的编号,取出的结果是SEL类型。
SEL:类成员方法的指针,与C的函数指针不一样,函数指针直接保存了方法的地址,而SEL只是方法的编号。
SEL消息机制的工作原理:
Objective-C 对象都是C 语言结构体,所有的对象都有一个isa的变量,而isa变量指向该对象的类。类其实也是实体的存在, 程序运行时每个类都有自己的存储空间,而isa 便指向这样一个类的空间,便建立了类和对象的对应关系,类空间包含了该类的成员变量以及方法实现,还包含指向父类空间的指针。
selector的数据类型是SEL,对应每个方法的位置的ID,当我们寻找方法的时候寻找的是方法的ID,存在一个方法和ID对应的methodList表来存储这种对应关系。
【赘述一些关于isa的知识】
所有实例对象中的isa指针指向类对象,类对象中的isa指针指向元类对象。(isa变量指向该对象的类。)
isa指向:对象isa->类,类isa->元类,元类isa->根元类,根元类isa->根元类自己,形成回路。
super class:对象super class指向对象super class。元类super class指向super class父类。根元类meta root class指向类NSObject。
什么情况会报unrecognized selector错误?iOS有哪些机制可避免这个错误。
- 调用该selector的对象被提前释放了;
- 没有定义调用的selector;
- 类的头文件中定义了selector,但是对应的源文件中没有实现,或者方法类型不一致(比如定义的是类方法,实现的是实例方法);
解决问题的方法:
在开发中 unrecognized selector sent to instance XXXXX 是非常常见的crash 类型。
objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:
Method resolution
objc运行时会调用+resolveInstanceMethod:或者+resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。
Fast forwarding
如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。
只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。
这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。
Normal forwarding
这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。
resolveInstanceMethod需要在类的本身上动态添加它本身不存在的方法,这些方法对于该类本身来说冗余的
forwardInvocation可以通过NSInvocation 的形式将消息转发给多个对象,但是其开销较大,需要创建新的NSInvocation 对象,并且forwardInvocation 的函数经常被使用者调用,来做多层消息转发选择机制,不适合多次重写
forwardingTargetForSelector可以将消息转发给一个对象,开销较小,并且被重写的概率较低,适合重写
选择了forwardingTargetForSelector 之后,可以将NSObject 的该方法重写,做以下几步的处理:
具体如下:
- hook forwardingTargetForSelector 方法
- 添加白名单,限制hook的范围,排除内部类,并自定义需要hook的类
- 创建桩类ForwardingTarget
- 为桩类动态添加对应的selector的imp,指向一个函数,返回NSNull 对象
- 将消息转移到该桩类对象ForwardingTarget 上
- 将hook 掉的crash 信息进行上报,方便发现问题,后期修复掉。
通过重写NSObject的forwardingTargetForSelector方法,我们就可以将无法识别的方法进行拦截并且将消息转发到安全的桩类对象中,从而可以使app继续正常运行。
<考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑>
<考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑>
<考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑-考虑>
具体的实现代码如下:
#import <Foundation/Foundation.h>
@interface ForwardingTarget : NSObject
@end
#import "ForwardingTarget.h"
#import <objc/runtime.h>
@implementation ForwardingTarget
id ForwardingTarget_dynamicMethod(id self, SEL _cmd) {
return [NSNull null];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
class_addMethod(self.class, sel, (IMP)ForwardingTarget_dynamicMethod, "@@:");
[super resolveInstanceMethod:sel];
return YES;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
id result = [super forwardingTargetForSelector:aSelector];
return result;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
id result = [super methodSignatureForSelector:aSelector];
return result;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
[super forwardInvocation:anInvocation];
}
- (void)doesNotRecognizeSelector:(SEL)aSelector {
[super doesNotRecognizeSelector:aSelector]; // crash
}
@end
#import <Foundation/Foundation.h>
@interface NSObject (DoesNotRecognizeSelectorExtension)
@end
#import "NSObject+DoesNotRecognizeSelectorExtension.h"
#import <objc/runtime.h>
#import "ForwardingTarget.h"
static ForwardingTarget *_target = nil;
@implementation NSObject (DoesNotRecognizeSelectorExtension)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_target = [ForwardingTarget new];;
not_recognize_selector_classMethodSwizzle([self class], @selector(forwardingTargetForSelector:), @selector(doesnot_recognize_selector_swizzleForwardingTargetForSelector:));
});
}
+ (BOOL)isWhiteListClass:(Class)class {
NSString *classString = NSStringFromClass(class);
BOOL isInternal = [classString hasPrefix:@"_"];
if (isInternal) {
return NO;
}
BOOL isNull = [classString isEqualToString:NSStringFromClass([NSNull class])];
BOOL isMyClass = [classString ...];
return isNull || isMyClass;
}
- (id)doesnot_recognize_selector_swizzleForwardingTargetForSelector:(SEL)aSelector {
id result = [self doesnot_recognize_selector_swizzleForwardingTargetForSelector:aSelector];
if (result) {
return result;
}
BOOL isWhiteListClass = [[self class] isWhiteListClass:[self class]];
if (!isWhiteListClass) {
return nil;
}
if (!result) {
result = _target;
}
return result;
}
#pragma mark - private method
BOOL not_recognize_selector_classMethodSwizzle(Class aClass, SEL originalSelector, SEL swizzleSelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzleMethod = class_getInstanceMethod(aClass, swizzleSelector);
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzleMethod),
method_getTypeEncoding(swizzleMethod));
if (didAddMethod) {
class_replaceMethod(aClass,
swizzleSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzleMethod);
}
return YES;
}
@end
unrecognized selector sent to instance XXXXX相关推荐
- -[XXXX encodeWithCoder:]: unrecognized selector sent to instance 0x12d931d10
2016-12-28 17:03:41.756 eVision论坛[9395:4643589] *** Terminating app due to uncaught exception 'NSInv ...
- 静态库调用中“unrecognized selector sent to instance”错误
在开发调用静态库的中,出现 "unrecognized selector sent to instance 0x2b5f90"的错误 -[__NSCFConstantString ...
- ios unrecognized selector sent to instance出现的原因和解决方案
概述:造成unrecognized selector sent to instance iphone,大部分情况下是因为对象被提前release了,在你心里不希望他release的情况下,指针还在,对 ...
- IOS微信API异常:unrecognized selector sent to instance 0x17005c9b0'
2019独角兽企业重金招聘Python工程师标准>>> 开发IOS整合微信API的时候,在运行程序的过程中可能会在注册你的APPID的时候抛出此异常而导致程序崩溃. 异常描述 [76 ...
- 【iOS】使用storyboard界面跳转报错:unrecognized selector sent to instance 0x7
使用storyboard直接model界面跳转的时候出现报错:unrecognized selector sent to instance 0x7... 网上查了相似的问题但是依旧没有找到解决方法,后 ...
- IOS微信API异常:unrecognized selector sent to instance 0x17005c9b0‘
IOS微信API异常:unrecognized selector sent to instance 0x17005c9b0' 参考文章: (1)IOS微信API异常:unrecognized sele ...
- unrecognized selector sent to instance
[iOS] Error Fixed : [__NSArrayI addObject:]: unrecognized selector sent to instance 当我创建了一个NSMutable ...
- 小萝莉说Crash(一):Unrecognized selector sent to instance xxxx
写在前面的:分享一篇文,原文地址:小萝莉说Crash(一):Unrecognized selector sent to instance xxxx -------------------------- ...
- 【小萝莉说Crash】第一期:Unrecognized selector sent to instance xxxx
大家好,我是来自Bugly Crash实验室的小萝莉(害羞ing),很高兴能和大家一起讨论关于移动终端App的Crash问题及解决方法. 在上次的"精神哥讲Crash"系列中,精神 ...
- 问题--[__NSCFNumber length]: unrecognized selector sent to instance 0x8b3c310’ - andy_shen
程序运行出现这个错误 : 'NSInvalidArgumentException', reason: '-[__NSCFNumber length]: unrecognized selector se ...
最新文章
- 对IP首部检验和的理解
- Android:Handler的消息机制
- who,cut,diff,which,whereis,locate,updatedb 命令的使用
- LearningR-XML
- mysql列别名引用_引用聚合列的MySQL别名
- L 苍天阻我寻你,此情坚贞如一(西南科技大学2021届新生赛)(线段树)
- django内置服务器
- 数据链路层的是三个基本问题
- pyqt5 界面切换
- 教育培训招生小程序源码
- 计算机远程桌面连接有几种方式,远程桌面连接的2种方法
- 每月一书(202106):《刻意练习》(一万小时定律正确吗)
- nlp文本预处理构建词汇表
- (转)零基础入门--中文命名实体识别
- html 穿越星空效果,html5 canvas绚丽3d星空飞行穿梭动画特效
- node.js下安装 webpack 的时候,出现:TypeError:this is not a typed array;
- linux c语言文件锁,Linux下glibc库文件锁:协同锁(advisory lock)和强制锁(mandatory lock)...
- android热更新bugly
- 解决linux下退格时,出现^H^H^H^H的问题
- EBS之Dataload常用命令