上一篇相关内容:OC底层探索 - 方法的底层原理
动态方法决议
一旦实现动态方法决议方法,必须给 sel
添加 imp
,否则会崩溃。
static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
// 判断当前cls是否是元类
if (! cls->isMetaClass()) {
// 非元类,所以尝试 [cls resolveInstanceMethod:sel]
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else {
// 元类,所以尝试 [nonMetaClass resolveClassMethod:sel]
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
// 如果 cache中 没找到 imp 的实现
// 尝试 [cls resolveInstanceMethod:sel]
if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
}
// 可能现在有缓存了,所以再尝试查询一次
// chances are that calling the resolver have populated the cache
// so attempt using it
return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
实例方法的动态方法决议
+ (BOOL)resolveInstanceMethod:(SEL)sel
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
SEL resolve_sel = @selector(resolveInstanceMethod:);
// 容错处理,判断该元类的继承链中是否有resolveInstanceMethod方法
// 如果自定义没实现,则会找到NSObject
if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
// Resolver not implemented.
return;
}
// 这里是消息发送,向 cls 发送 resolveInstanceMethod: 这个方法的消息
// 所以我们可以在cls中实现,+(BOOL)resolveInstanceMethod:(SEL)sel 来处理实例方法找不到的情况
// 比如给 sel 添加一个 imp 的实现
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
// 去 cache 中查找 sel 的 imp,如果resolveInstanceMethod中有
// 添加 imp的实现,那么 cache中就可以找到了
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
// 异常情况,打印错误信息
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
示例
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(func2)) {
// 基于runtime,向类对象添加一个方法
class_addMethod(self.class, sel, class_getMethodImplementation(self.class, @selector(func4)), "v@:");
return YES;
}
return [super resolveClassMethod:sel];
}
- (void)func4 {
NSLog(@"func4");
}
类方法的动态方法决议
+ (BOOL)resolveClassMethod:(SEL)sel
/***********************************************************************
* resolveClassMethod
* Call +resolveClassMethod, looking for a method to be added to class cls.
* cls should be a metaclass.
* Does not check if the method already exists.
**********************************************************************/
// 这里的 cls 是元类
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
// 容错处理,判断该元类的继承链中是否有resolveClassMethod方法
// 如果自定义没实现,则会找到NSObject
if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
// Resolver not implemented.
return;
}
Class nonmeta;
{
// 得到元类的对应的类对象,因为我们无法在元类中些代码
// 所以 + (BOOL)resolveClassMethod:(SEL)sel 只能写在类对象中
// 那么,我们就需要向类对象发送消息了
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta);
}
}
// 这里向类对象发送消息
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// 从cache中获取 imp
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
// 异常处理
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
示例
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(func1)) {
// 基于runtime,向元类对象添加一个方法
class_addMethod(objc_getMetaClass("AClass"), sel, class_getMethodImplementation(objc_getMetaClass("AClass"), @selector(func3)),"v@:");
return YES;
}
return [super resolveClassMethod:sel];
}
+ (void)func3 {
NSLog(@"func3");
}
消息转发
在动态方法决议之后,如果还是没有找到方法的实现。那么就会进入到消息转发阶段。
消息转发,顾名思义就是,当类自己没有实现方法,那么可以指定一个实现了方法的类/实例来执行方法,也就是把消息转发出去。
消息转发有2种,快速消息转发
和 慢速消息转发
。
系统会首先尝试 快速消息转发
, 如果 快速消息转发
没有找到,那么会进入 慢速消息转发
, 如果慢速消息转发
也未找到,则会执行doesNotRecognizeSelector
抛出异常。
快速消息转发 forwardingTargetForSelector
/* AClass */
@interface AClass : NSObject
- (void)func1;
+ (void)func2;
@end
@implementation AClass
- (void)func1 {
NSLog(@"%@ %s", [self class], __func__);
}
+ (void)func2 {
NSLog(@"%@ %s", [self class], __func__);
}
@end
/* BClass */
@interface BClass : NSObject
- (void)func1;
+ (void)func2;
@end
@implementation BClass
// 实例方法的快速消息转发
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(func1)) {
// 返回的是实例对象
return [AClass new];
}
return nil;
}
// 类方法的快速消息转发
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(func2)) {
// 返回的是类对象
return [AClass class];
}
return nil;
}
@end
BClass *b = [BClass new];
[b func1]; // AClass -[AClass func1]
[BClass func2]; // AClass +[AClass func2]
快速消息转发
的实现可以写在类
、父类
或者它们的分类
中,都能起到作用
慢速消息转发 methodSignatureForSelector
需要注意的是methodSignatureForSelector
返回的是一个方法签名,需要结合 forwardInvocation
方法来处理才行。没有实现forwardInvocation
会崩溃。
/* AClass */
@interface AClass : NSObject
- (void)func1;
+ (void)func2;
@end
@implementation AClass
- (void)func1 {
NSLog(@"%@ %s", [self class], __func__);
}
+ (void)func2 {
NSLog(@"%@ %s", [self class], __func__);
}
@end
/* BClass */
@interface BClass : NSObject
- (void)func1;
+ (void)func2;
@end
@implementation BClass
// 类方法的慢速消息转发,这里返回一个有效的方法签名
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"什么都不做");
}
// 实例方法的慢速消息转发,这里返回一个有效的方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
AClass *a = [AClass new];
if ([self respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self];
}else if ([a respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:a];
}else {
NSLog(@"该方法未处理");
}
}
@end
BClass *b = [BClass new];
[b func1]; // AClass -[AClass func1]
[BClass func2]; // 什么都不做
可见,慢速消息转发
中 forwardInvocation
使用非常灵活,只要有实现就不会崩溃,可以什么都不做,也以自己处理,也可以转发给别人。
慢速消息转发
中方法调用顺序是:先methodSignatureForSelector
, 后 forwardInvocation