动态方法决议
一:源码分析
方法查找未找到时候,会来到方法决议。
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
进入_class_resolveMethod(cls, sel, inst);后,
**********************************************************************/
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst); // 已经处理
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// 对象方法 决议
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
/***********************************************************************
对象方法的决议进入_class_resolveInstanceMethod,类方法的方法决议进入到_class_resolveClassMethod(cls, sel, inst);
但是,如果在类方法决议时未找到有处理的 _class_resolveClassMethod 时,会进入到对象方法方法决议中,源码中下图:
二.使用实例
1.类方法 在.main 中调用 Student 类方法sayLove,Love在Student继承的LGPerson中声明,但是未实现。
// 类方法 如果找不到 - 动态方法决议
// 类 - 元类 - NSObject
// 1: resolveClassMethod 你是否处理 - 只要注意元类
// 2: resolveClassMethod 没有处理 - resolveInstanceMethod
+ (BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"来了类方法:%s - %@",__func__,NSStringFromSelector(sel));
if (sel == @selector(sayLove)) {
NSLog(@"说- 说你你爱我");
IMP sayHIMP = class_getMethodImplementation(self, @selector(sayObjc));
Method sayHMethod = class_getClassMethod(self, @selector(sayObjc));
const char *sayHType = method_getTypeEncoding(sayHMethod);
// 类方法在元类 objc_getMetaClass("LGStudent")
return class_addMethod(self, sel, sayHIMP, sayHType);
}
return [super resolveClassMethod:sel];
}
2.对象方法 在.main 中创建Student 的对象 调用 对象方法saySomething,saySomething在Student继承的LGPerson中声明,但是未实现。
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"来了老弟:%s - %@",__func__,NSStringFromSelector(sel));
if (sel == @selector(saySomething)) {
NSLog(@"说话了");
IMP sayHIMP = class_getMethodImplementation(self, @selector(sayHello));
Method sayHMethod = class_getInstanceMethod(self, @selector(sayHello));
const char *sayHType = method_getTypeEncoding(sayHMethod);
return class_addMethod(self, sel, sayHIMP, sayHType);
}
return [super resolveInstanceMethod:sel];
}
至此就不会出现方法崩溃,崩溃打印由于方法未找到: unrecognized selector sent to instance 。
消息转发机制
方法的打印
方法查找源码中有这样一段代码
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
进入到log_and_fill_cache中,
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
#if SUPPORT_MESSAGE_LOGGING
if (objcMsgLogEnabled) {
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if (!cacheIt) return;
}
#endif
cache_fill (cls, sel, imp, receiver);
}
在进入到logMessageSend中:
查看可以打印消息的赋值的地方: 取决于flag值,
用instrumentObjc方法打印崩溃的信息,
可以发现方法崩溃前,调用的一些其他的方法。
快速转发
Cooci_消息转发机制.png
1.forwardingTargetForSelector
forwardingTargetForSelector无法调入源码中,通过苹果开发文档中给出解释。
2.测试实例
在.main 中创建Student 的对象 调用 对象方法saySomething,saySomething在Student继承的LGPerson中都未声明和实现,但是在继承NSObject的LGTeache中实现。
// 别人可能有?
// 快速流程 - 交给一个对象来处理
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s -- %@",__func__,NSStringFromSelector(aSelector));
if (aSelector == @selector(saySomething)) {
return [LGTeacher alloc];
}
return [super forwardingTargetForSelector:aSelector];
}
慢速转发
1.methodSignatureForSelector
如果快速转发消息中未作处理,则会来到慢速转发的流程中。 methodSignatureForSelector同样源码中无法探寻,可以通过苹果开发文档查看如下:
2.测试实例
在.main 中创建Student 的对象 调用 对象方法saySomething,saySomething在Student继承的LGPerson中都未声明和实现,但是在继承NSObject的LGTeache中实现
// 慢速转发 -- 漂流瓶
// unre - 不崩溃
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@"%s -- %@",__func__,NSStringFromSelector(aSelector));
if (aSelector == @selector(saySomething)) { // v @ :
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
//
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s ",__func__);
// 事情 - 事务 - 秘书 - 失效
// 系统本质
// SEL aSelector = [anInvocation selector];
//
// if ([[LGTeacher alloc] respondsToSelector:aSelector])
// [anInvocation invokeWithTarget:[LGTeacher alloc]];
// else
// [super forwardInvocation:anInvocation];
}