我们在上篇文章中提到,要处理 obj 找不到 对应的 方法的时候,有3种方法可以处理,从而不让系统crash.
这第一种方法就是 动态解析机制。主要函数为
+ (BOOL) resolveInstanceMethod:(SEL)sel
//IMP 函数, obj 表示 self, _cmd 表示当前方法
void DogBark(id obj, SEL _cmd, id content) {
NSLog(@"The Dog is Barking: %@", content);
}
+ (BOOL) resolveInstanceMethod:(SEL)sel {
if (sel == @selector(Bark)) {
class_addMethod([self class], sel, (IMP)(DogBark), "v@:@");
//v@:@ 表示是void 类型函数,后面接一个 参数
return YES;
}
else {
return [super resolveInstanceMethod:sel];
}
}
示例方法如下:
比如我们调用 Bark 方法,其实 Bark 方法并没有在我们的类中提供。但是我们提供了IMP 叫 DogBark , 在动态解析的过程中,我们把 对Bark的调用, 替换为 对DogBark 这个IMP的调用。
[self performSelector:@selector(Bark) withObject:@"555"];
为什么用 performSelector 呢,因为 如果直接写 [self Bark] 会编译不通过,编译器的自动检查机制会直接不让你调用没有定义的方法。所以这里是动态调用。
然后我们可以看到, 对 Bark的调用,成功动态解析为了 对 DogBark 这个 IMP的调用。
如果我们有动态解析,那么返回 TRUE, 否则, 返回 super的实现。
下面我们研究下
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types) 这个函数,这个函数用来动态给 一个类 的 name 方法 添加 一个IMP 实现。
最后一个参数,表示 IMP的类型, V@: 表示 返回值为 Void, 参数为空。 V@:@ 表示 返回值为Void, 参数为 一个 obj, 详见 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1
另外我们看下 class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) 这个函数,这个函数表示我们获得 一个类的 selector 的 IMP run time 函数。
看看下面的代码会发生什么?分别定义 Dog 和 Pig 两个类,其中 Dog 类没有任何方法, 而 Pig 类有一个 Bark 方法。
@interface Dog : NSObject
@end
@implementation Dog
@end
@interface Pig : NSObject
-(void) Bark;
@end
@implementation Pig
-(void) Bark {
NSLog(@"Pig is Barking");
}
@end
然后我们把 Pig 类的 Bark 的IMP 实现, 动态绑定到 Dog 的 Bark 方法上,看看结果是什么呢?
class_addMethod([Dog class], @selector(Bark), class_getMethodImplementation([Pig class], @selector(Bark)), "v@:");
Dog * myDog = [[Dog alloc] init];
[myDog performSelector:@selector(Bark)];
神奇的事情发送了, 我们调用Dog 的 Bark 方法, 但是实际上执行的却是 Pig 的 Bark 方法。
我们再来看看
class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types) 这个函数,
顾名思义, 我们直接把 Dog 的 Bark 函数 替换为 Pig 的 Bark IMP实现, 也可以实现我们同样的效果。