消息转发机制
查看方法帮助
command+右键 show Quick Help 然后 command+shift+0
消息发送、查找
objc_msgSend,当方法的IMP没有寻找到时,首先进入动态方法决议,在 lookUpImpOrForward 方法内部
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
。。。。。。
// No implementation found. Try method resolver once.
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 就是我们进行方法解析的步骤,在方法内部,根据当前类是否为元类,来判断当前所执行的方法是对象方法还是类方法,从而进行下一步解析,并且返回一个 Bool 值作为
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);
}
}
}
在两种方法解析的内部,其原理就是向当前类发送一个 SEL_resolveInstanceMethod 或者 SEL_resolveClassMethod 消息(二者的定义在 objc-runtime.mm 文件中),并且通过注释也可以知道,这两个方法,在我们的根类NSObject中已经被实现
SEL SEL_resolveInstanceMethod = NULL;// 解析实例方法
SEL SEL_resolveClassMethod = NULL;//解析类方法
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
如果通过查找找不到方法 同时 方法解析也没有处理,那么程序就会崩溃。如果不想让崩溃,又找不到方法的情况下,就可以在方法解析的地方进行判断并指定给这个方法一个 IMP 。
因为在方法查找不到的时候系统会自动调用这两个方法( resolveClassMethod 或者 resolveInstanceMethod ),在这里给了开发者一次容错以及避免crash的机会,需要我们重写并且在方法内部给 未查找到对应实现的的方法 动态添加一个已经存在的IMP
/***********************************************************************
* _class_resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
调用+resolveInstanceMethod,寻找一个方法添加到类cls。
cls可以是元类,也可以是非元类。
*不检查方法是否已经存在。
**********************************************************************/
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
如果找到了返回
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
。。。。一些判断打印
}
消息转发的流程
方法转发
oc的方法调用,在底层会通过 objc_msgSend 进行消息发送
依次进行缓存快速查找 imp,以及类的方法列表查找之后,如果仍然没有找到目标 method,那么则进入消息转发流程
1)、先找自己
2)、动态方法决议 (有时候会特殊处理,如果自己没处理看动态解析有没有处理)
3)、快速流程 ,别人处理 (如果也没有特殊处理,看是否是交给别人进行了处理)
4)、慢速转发 (如果也没有交给别人处理,就相当于一个漂流瓶,谁捡到谁去处理,只要不崩溃就行。这些方法都会堆积到当前的 -(void)forwardInvocation:(NSInvocation *)anINvocation))
方法命名:标记_路由_事务 例如: lg_home_sayHello
1、动态方法决议
- 类里面 +(Bool)resolveInstanceMethod:(SEL)sel{ return [super resolveInstanceMethod:sel] }
- 类里面 +(Bool)resolveClassMethod:(SEL)sel{ 类方法来了 return [super resolveClassMethod:sel]}
- 如果在动态决议里面处理了对应的方法(sel),那么resolve…只走一次,如果不处理,在下层就还会有其他的处理,这个方法就会走两次
- 类方法 如果找不到 – 动态方法决议
- 类 – 元类 – NSObject
- resolveClassMethod 你是否处理 – 只要注意元类
- resolveClassMethod 没有处理 – resolveInstanceMethod
- 上图中,为什么在类方法决议中走过之后,还要再去进行一次元类查找?1、为了容错,2、避免重名。
- lookUpImpOrForwrad(class cls, SEL sel 。。。。。。)会走到这个方法里面进行处理
- _class_resolveMethod(class cls, …….)
动态决议 补充
先去正常的查找方法
如果没有找到走动态方法决议,去看是否有特殊处理。
先判断传进去的是不是元类,如果是元类就是类方法。
如果特殊处理过了(也就是给他添加了一个sel - imp),这个时候就会返回imp,然后再次去查找imp
2、快速转发
- forwardingTargetForSelector
- 返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续查找其他流程
- 系统给了个将这个SEL转给其他对象的机会
- 如果交给的对象也无法实现,就进行慢速转发流程
3、慢速转发
- methodSignatureForSelector
- 这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。
- 返回SEL方法的签名,返回的签名是根据方法的参数来封装的
- forwardInvocation
- 真正执行methodSignatureForSelector:返回的NSMethodSignature。这个函数里面可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。(forwardingTargetForSelector只能从Selector的形式专享一个对象)
- /Applications/xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework
-
- objc_msgSend 查找imp 缓存
- 慢速递归
- 、、、、、、、、、、、、、、、
- 查找你是不是有特殊处理
- 你有没有交给别人进行处理
- 都没有意味着不想处理
- methodSignatureForSelector 方法知道,底层方法签名很重要,需要告诉我
- forwardInvocation:anInvocation 有这么个接口提供一个事务管理器,所有事务都在这里
- 不想进行崩溃,如果想处理,就走上边的接口去处理
- 如果就是不想处理,就不会走doesNotRecognizeSelector
4、消息无法处理报错
- 消息无法处理报错
- doesNotRecognizeSelector
- 流程图
- 问题
- 动态方法决议没有处理 会来两次
- resolveClassMethod 走完resolvenInstanceMethod
- 其他
- 进行反汇编,得到相应伪代码