对象、消息、运行期--12:runtime消息转发

2023-11-22 08:20:31 浏览数 (2)

消息转发

消息转发分为俩大阶段

  • 动态方法解析
  • 完整的消息转发机制
  • 消息转发全流程:
    1. 若对象无法响应某个选择器,则进入消息转发流程

    2.通过运行期间的动态方法解析,可以再需要用到某个方法时再将其加入类中 3.对象可以把其无法解读的某些选择器转交给其他对象处理 4.经过上述两步,如果还是不能处理选择器,那就启动完整的消息转发机制

消息转发全流程.png

动态方法解析(动态添加方法)

当一个实例对象调用一个不存在的方法,为了程序不carsh,我们可以动态的为其添加方法

代码语言:javascript复制
//Phone并没有say方法的实现
Phone *phone = [[Phone alloc]init];
[phone say];
代码语言:javascript复制
//程序carsh报告
 -[Phone say]: unrecognized selector sent to instance 0x600000005b90
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Phone say]: unrecognized selector sent to instance 0x600000005b90'
  • 第一步:对象在收到无法解读的消息后,首先会调用 (BOOL)resolveInstanceMethod:(SEL)sel 或者 (BOOL)resolveClassMethod:(SEL)sel
代码语言:javascript复制
#import "Phone.h"
#import <objc/runtime.h>
@implementation Phone
void sayHello(){
    NSLog(@"sayHello");
}
  (BOOL)resolveInstanceMethod:(SEL)sel{
    //判断方法名是否是sayHello,有则加
    if ([NSStringFromSelector(sel) hasSuffix:@"sayHello"]) {
        // 第一个参数:给哪个类添加方法
        // 第二个参数:方法名
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值 参数类型) v:void @:对象->self :表示SEL->_cmd
        class_addMethod([self class], sel, (IMP)sayHello, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

完整的消息转发机制

  • 第二步:第一步执行完,如果没有新增方法,运行期系统会把这个消息转给其他接收者处理,系统会调用这个方法
代码语言:javascript复制
- (id)forwardingTargetForSelector:(SEL)aSelector

此时,我们需要找一个与Phone相关连的类去处理这个消息,如果这个相关联的对象可以处理这个消息,则返回这个对象,若不能则返回nil。

代码语言:javascript复制
#import "Phone.h"
#import <objc/runtime.h>
#import "iPhone.h"
@implementation Phone
//第一步
  (BOOL)resolveInstanceMethod:(SEL)sel{
   return [super resolveInstanceMethod:sel];
}
//第二步
- (id)forwardingTargetForSelector:(SEL)aSelector{
    iPhone *phone = [[iPhone alloc]init];
    if ([phone respondsToSelector:aSelector]) {
        return phone;
    }
    return [super forwardingTargetForSelector: aSelector];
}
  • 第三步:如果第二步返回nil,那就会走到最后一步,这步也是代价最大的一步
代码语言:javascript复制
#import "Phone.h"
#import <objc/runtime.h>
#import "iPhone.h"
@implementation Phone
//第一步
  (BOOL)resolveInstanceMethod:(SEL)sel{
   return [super resolveInstanceMethod:sel];
}
//第二步
- (id)forwardingTargetForSelector:(SEL)aSelector{
    return [super forwardingTargetForSelector: aSelector];
}
//第三步
//首先寻找方法签名,如果没有,则会调用系统根类的doesNotRecognizeSelector:方法,不会进入到下面的消息分发.
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if(aSelector == @selector(sayHello)){
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}
//如果上面的方法签名找到了,则会调用这个方法.将消息传递给其他对象,可以传递给多个对象,通过anInvocation拿到相应信息
-(void)forwardInvocation:(NSInvocation *)anInvocation{
    if (anInvocation.selector == @selector(sayHello)){
        iPhone *phone = [[iPhone alloc]init];
        [anInvocation invokeWithTarget:phone];
    }
}
//寻找方法签名,如果没有找到,则回调这个方法
- (void)doesNotRecognizeSelector:(SEL)aSelector{
    NSLog(@"消息无法响应");
}

参考

Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法

0 人点赞