一、添加 方法 / 函数 - 动态方法解析(Dynamic Method Resolution)
-
对象方法
没有 声明实现, 添加 对象方法 / 函数
+resolveInstanceMethod:
* 在 对象方法 里面 调
* 此处的方法名要与下面判断时的 一致,有参无参都要一致
[self performSelector:@selector(noInstenceMethod:)];
* 这里一定是 +resolveInstanceMethod:
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@" 1 * resolveInstanceMethod - 方法 %@ * ",NSStringFromSelector(sel));
* 这里 要与上面的 对象方法调用处,方法名一致,有参无参都要一致
if (sel == @selector(noInstenceMethod:)) {
/**
运行时方法:向指定类中添加特定方法实现的操作
@param cls 被添加方法的类
@param name selector方法名 - 直接填此方法带的 sel
@param imp 指向实现方法的函数指针
@param types imp函数实现的返回值与参数类型 - 实测,好像不写或写 nil 均可?!
@return 添加方法是否成功 - 直接写 YES,在这里写就是为了添加方法,干嘛还NO?!
(但是实测,返回两者,都可运行成功,可手动写代码亲自试验)
(如果真的返回 NO的话,就关系到【消息转发 - Forwarding】了)
*/
* 哪种添加方法写法 写在上面,就找谁添加的 方法 或 函数 实现 *
* 写 两种 没用,任选其一 *
* imp 这种写法,只能添加 函数
// class_addMethod(self, sel, hereInstenceMethod, "v@");
* imp 这种写法,只能添加 对象方法
Method addM = class_getInstanceMethod(self, @selector(hereInstenceMethod));
class_addMethod(self,
sel,
method_getImplementation(addM),
method_getTypeEncoding(addM));
return YES;
}
return [super resolveInstanceMethod:sel];
}
//void hereInstenceMethod()
//{
// NSLog(@" 2 * hereInstenceMethod 函数 * ");
//}
- (void)hereInstenceMethod
{
NSLog(@" 2 * hereInstenceMethod - 方法 * ");
}
-
类方法
没有 声明实现, 添加 类方法 / 函数
+resolveClassMethod:
* 类名调用
* 此处的方法名要与下面判断时的 一致,有参无参都要一致
[类名 performSelector:@selector(noClassMethod:)];
* 这里一定是 +resolveClassMethod:
+ (BOOL)resolveClassMethod:(SEL)sel
{
NSLog(@" 1 * resolveClassMethod + 方法 %@ * ",NSStringFromSelector(sel));
* 这里 要与上面的 类方法调用处,方法名一致,有参无参都要一致
if (sel == @selector(noClassMethod:)) {
* 哪种添加方法写法 写在上面,就找谁添加的 方法 或 函数 实现 *
* 写 两种 没用,任选其一 *
* imp 这种写法,只能添加 函数
* 注意,这里与 对象方法的添加方法里,self 的写法不同
// class_addMethod(object_getClass(self), sel, hereClassMethod, "v@");
* imp 这种写法,只能添加 + 方法
* 注意,这里与 对象方法的添加方法里,self 的写法不同
* 要这样写:object_getClass(self) :)
Method addM = class_getInstanceMethod(self, @selector(hereClassMethod));
class_addMethod(object_getClass(self),
sel,
method_getImplementation(addM),
method_getTypeEncoding(addM));
return YES;
}
return [super resolveClassMethod:sel];
}
//void hereClassMethod()
//{
// NSLog(@" 2 * hereClassMethod + 函数 * ");
//}
+ (void)hereClassMethod
{
NSLog(@" 2 * hereClassMethod + 方法 * ");
}
注意 + / -
方法的 添加 方法 / 函数
,写法有不同
- class_addMethod方法中的特殊参数“v@”,具体可参考这里
OC Runtime-Type Encodings.png
二、消息转发 - 备用接收者 - 消息接收者重定向
-
对象方法
在本类中 没有 声明实现,将此消息转发到别的类实现
-forwardingTargetForSelector
* 创建一个备用接收者的类 `#import "TestForwardingM.h"`
* 且在此类中实现 `-hereInstanceMethod:`
* 在其他类中 进行`对象方法`调用,引入 `TestForwardingM.h` 头文件
[self performSelector:@selector(hereInstanceMethod:)withObject:@"VampireJune"];
/*
1. 如果没有实现 `+resolveInstanceMethod`
会直接进入到 `+forwardingTargetForSelector`
2. 只要实现了 `+resolveInstanceMethod`
1> 只要添加了方法成功,就不会去 `+forwardingTargetForSelector`
无论返回 `YES / NO`
2> 只要没有添加方法,都会去 `+forwardingTargetForSelector`
无论返回 `YES / NO / [super resolveInstanceMethod:sel]`
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@" 1 * %s %@ * ",__func__,NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@" 2 * %s %@ * ", __func__, NSStringFromSelector(aSelector));
if (aSelector == @selector(hereInstanceMethod:)) {
// 返回 `TestForwardingM` 实例对象
return [[TestForwardingM alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
* `TestForwardingM.m` 中
@implementation TestForwardingM
- (void)hereInstanceMethod:(id)aid
{
NSLog(@" 3 * %s %@ * ",__func__,aid);
// 打印
// 3 * -[TestForwardingM hereInstanceMethod:] VampireJune *
}
@end
-
类方法
在本类中 没有 声明实现,将此消息转发到别的类实现
+forwardingTargetForSelector
* 创建一个备用接收者的类 `#import "TestForwardingM.h"`
* 且在此类中实现 `+hereClassMethod:`
* 在其他类中 进行 `类方法` 调用,引入 `TestForwardingM.h` 头文件
[类名 performSelector:@selector(hereClassMethod:) withObject:@"VampireJune"];
/*
1. 如果没有实现 `+resolveClassMethod`
会直接进入到 `+forwardingTargetForSelector`,不能直接敲出此方法
需要敲出 `-forwardingTargetForSelector`,再将 `-` 改为 `+`
【实测是这样。2018.11.02(周五),Xcode 10.0(10A255)】
2. 只要实现了 `+resolveClassMethod`
1> 只要添加了方法且成功,就不会去到 `+forwardingTargetForSelector`
无论返回 `YES / NO`
2> 只要没有添加方法,都会去到 `+forwardingTargetForSelector`
无论返回 `YES / NO / [super resolveClassMethod:sel]`
*/
+ (BOOL)resolveClassMethod:(SEL)sel
{
NSLog(@" 1 * %s %@ * ",__func__,NSStringFromSelector(sel));
return [super resolveClassMethod:sel];
}
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@" 2 * %s %@ * ", __func__, NSStringFromSelector(aSelector));
if (aSelector == @selector(hereClassMethod:)) {
// 返回一个类对象
return [TestForwardingM class];
}
return [super forwardingTargetForSelector:aSelector];
}
* `TestForwardingM.m` 中
@implementation TestForwardingM
+ (void)hereClassMethod:(id)aid
{
NSLog(@" 3 * %s %@ * ",__func__,aid);
// 打印
// 3 * +[TestForwardingM hereClassMethod:] VampireJune *
}
@end
三、消息重定向(重定向消息类型,但目标只能重定向给 实例对象)
- 当
+resolveInstanceMethod:
和
-forwardingTargetForSelector:
两种方法都没能生效时,此时 Runtime
会给这次消息最后一次寻找IMP(方法实现)的机会
-methodSignatureForSelector:
-forwardInvocation:
* 创建一个备用接收者的类 `#import "TestForwardingM.h"`
* 且在此类中实现 `-hereInstanceMethod:`
* 对象方法调用
[self performSelector:@selector(hereInstanceMethod:)withObject:@"VampireJune"];
* 下面这两个方法可不实现,只这样返回`super`,主要为了最后的发现与总结
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@" 1 * %s %@ * ",__func__,NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@" 2 * %s %@ * ", __func__, NSStringFromSelector(aSelector));
return [super forwardingTargetForSelector:aSelector];
}
* 上面这两个方法可不实现
* 下面的方法是 消息重定向 的 `关键操作` :)
* 这个方法会返回 方法的 `参数和返回值类型`,是一个 `方法签名`
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSLog(@" 3 * %s %@ * ", __func__, NSStringFromSelector(aSelector));
if (aSelector == @selector(hereInstanceMethod:)) {
// 如果是带参数的方法,一定要有 `:`
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
* 如果 `-methodSignatureForSelector:` 返回了 `方法签名`
* `Runtime` 就会创建一个 `NSInvocation` 对象并发送 `-forwardInvocation:` 消息给目标对象。
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sel = anInvocation.selector;
NSLog(@"4 * %s %@ * ", __func__, NSStringFromSelector(sel));
// 打印
// 4 * -[Art_Market_VC forwardInvocation:] hereInstanceMethod: *
TestForwardingM *tm = [[TestForwardingM alloc] init];
// 如果 `TestForwardingM` 类的实例对象 可以响应这个方法
if ([tm respondsToSelector:sel]) {
[anInvocation invokeWithTarget:tm];
}else{ // 否则 就崩溃
[self doesNotRecognizeSelector:sel];
}
}
* `TestForwardingM.m` 中
@implementation TestForwardingM
- (void)hereInstanceMethod:(id)aid
{
NSLog(@" - 完整消息转发 * %s %@ * ",__func__,aid);
// 打印
// - 完整消息转发 * -[TestForwardingM hereInstanceMethod:] VampireJune *
}
@end
消息重定向 - 发现与总结
1. 先看打印顺序,会发现 `1 *` 打印了两次,这不是打印 Bug
2018-11-02 16:30:08.977401+0800 [14127:219552] 1 * +[类名 resolveInstanceMethod:] hereInstanceMethod: *
2018-11-02 16:30:08.977537+0800 [14127:219552] 2 * -[类名 forwardingTargetForSelector:] hereInstanceMethod: *
2018-11-02 16:30:08.977637+0800 [14127:219552] 3 * -[类名 methodSignatureForSelector:] hereInstanceMethod: *
2018-11-02 16:30:08.977760+0800 [14127:219552] 1 * +[类名 resolveInstanceMethod:] _forwardStackInvocation: *
2018-11-02 16:30:08.977870+0800 [14127:219552] 4 * -[类名 forwardInvocation:] hereInstanceMethod: *
2018-11-02 16:30:08.978527+0800 [14127:219552] 完整消息转发 * -[TestForwardingM hereInstanceMethod:] VampireJune *
2. 如果都实现了下面两个方法
`+resolveInstanceMethod:` 和 `-forwardingTargetForSelector:`
而且都 仅仅 返回了 super,没有`添加方法`或`消息转发`的操作
3. 那么 第二次 调 `1 -> +resolveInstanceMethod:` 是
给 `4 -> -forwardInvocation:` 发消息
说明就是 给这次消息发送 `最后一次` 寻找IMP的机会,启用完整的消息转发机制
这就是:消息重定向。
4. 从以上的代码中就可以看出
> `forwardingTargetForSelector` 仅支持 `一个对象` 的返回,也就是说消息只能被转发给 `一个对象`
> 而 `forwardInvocation` 可以将消息同时转发给 `任意多个对象`
这就是两者的最大区别