上文了解了方法的查找流程,当IMP找到不到时,Runtime还给了补救的措施,动态方法决议 和 消息转发(resolve and forwarding)
1:动态方法决议 resolve
实现方法:
#import <objc/message.h>
@implementation LGStudent
- (void)sayHello{
NSLog(@"替补方法:%s",__func__);
}
+ (void)sayObjc{
NSLog(@"替补方法:%s",__func__);
}
+ (BOOL)resolveClassMethod:(SEL)sel {
NSLog(@"来了 resolveClassMethod :%s - %@",__func__,NSStringFromSelector(sel));
if (sel == @selector(sayLove)) {
//获取元类 ,添加方法需要添加到 元类里 Meta Class
Class cls = objc_getMetaClass("LGStudent");
//获取替代方法的IMP
IMP imp = class_getMethodImplementation(cls, @selector(sayObjc));
//获取替代方法的 method
Method method = class_getClassMethod(cls, @selector(sayObjc));
//获取替代方法的 encoding type 方法签名
const char *type = method_getTypeEncoding(method);
return class_addMethod(cls, sel, imp, type);//添加方法到元类里
}
return [super resolveClassMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"来了 resolveInstanceMethod :%s - %@",__func__,NSStringFromSelector(sel));
if (sel == @selector(saySomething)) {
//获取类 ,添加方法需要添加到 元类里 Meta Class
Class cls = self;
//获取替代方法的IMP
IMP sayhimp = class_getMethodImplementation(cls, @selector(sayHello));
//获取替代方法的 method
Method sayhmethod = class_getInstanceMethod(cls, @selector(sayHello));
//获取替代方法的 encoding type 方法签名
const char *type = method_getTypeEncoding(sayhmethod);
return class_addMethod(cls, sel, sayhimp, type);
}
return [super resolveInstanceMethod:sel];//添加方法到类里
}
@end
源码:runtime-new.mm
/***********************************************************************
* resolveMethod_locked
* Call +resolveClassMethod or +resolveInstanceMethod.
*
* Called with the runtimeLock held to avoid pressure in the caller
* Tail calls into lookUpImpOrForward, also to avoid pressure in the callerb
**********************************************************************/
static NEVER_INLINE IMP resolveMethod_locked(id inst, SEL sel, Class cls, int behavior) {
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
if (! cls->isMetaClass()) {//cls 不是元类
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
} else {//cls 是元类
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
if (!lookUpImpOrNil(inst, sel, cls)) {//此处有可能去 resolveInstanceMethod 查找
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
源码中可以看到,调用类方法的话,会到resolveClassMethod
去查找是否添加补救的方法,找不到的话,也有可能到resolveInstanceMethod
去查找是否添加方法。
猜想:在NSObject的分类里,在resolveInstanceMethod ``resolveClassMethod
中可以添加拦截补救方法,适当的可以起到拦截Crash问题,然后让其回归到 首页 我的页面。使用NSStringFromSelector(sel)
获取sel 方法名,获取前缀,来拦截。
2: 消息转发Forwarding
在方法查找流程的源代码IMP lookUpImpOrForward()
最后,查找IMP找不到时,会有一个imp = (IMP)_objc_msgForward_impcache; //Use forwarding(汇编)
。该方法在动态方法决议之后,lldb打印unrecognized selector sent to class 0x100003318
之前。
查找该方法踪迹:
1:通过debug汇编断点
的方式我们可以定位到消息转发的重载函数forwardInvocation 和 methodForSelector方法。
2:通过extern void instrumentObjcMessageSends(BOOL flag);
打印log的方式可以找到某个方法调用前后,调用了那些方法。
测试上面方法2:
//引用instrumentObjcMessageSends函数
extern void instrumentObjcMessageSends(BOOL flag);
int main(int argc, const char * argv[]) {
Person *p = [Person new];
//开启OC消息日志
instrumentObjcMessageSends(true);
//发送消息
[p sayInstance];
//关闭OC消息日志
instrumentObjcMessageSends(false);
return 0;
}
然后 找到msgSend-,分析日志,路径在/tmp路径下回生成名为msgSend-
//摘取了关键的日志信息
+ Person NSObject resolveInstanceMethod:
+ Person NSObject resolveInstanceMethod:
- Person NSObject forwardingTargetForSelector:
- Person NSObject forwardingTargetForSelector:
- Person NSObject methodSignatureForSelector:
- Person NSObject methodSignatureForSelector:
- Person NSObject class
+ Person NSObject resolveInstanceMethod:
+ Person NSObject resolveInstanceMethod:
- Person NSObject doesNotRecognizeSelector:
- Person NSObject doesNotRecognizeSelector:
- Person NSObject class
......
发现 forwardingTargetForSelector methodSignatureForSelector
两个方法,而两个方法是在NSObject中的,在子类中可以override。
PS:知识补充:对象的获取, 地址打印。
//实例对象
LGStudent *student = [[LGStudent alloc] init];
NSLog(@"地址: %p",student);// 实例对象student 地址0x101905400
//类对象
NSLog(@"地址: %p",[student class]);// 类对象地址0x100003328
NSLog(@"地址: %p",[LGStudent class]);// 类对象地址0x100003328
NSLog(@"地址: %p",[[LGStudent class]class]);// 类对象地址0x100003328
NSLog(@"地址: %p",objc_getClass("LGStudent"));// 类对象地址0x100003328
NSLog(@"地址: %p",[objc_getClass("LGStudent") class]);// 类对象地址0x100003328
//元类对象
NSLog(@"地址: %p",objc_getMetaClass("LGStudent"));// 元类对象地址0x100003300
NSLog(@"地址: %p",[objc_getMetaClass("LGStudent") class]);// 元类对象地址0x100003300
网友评论