美文网首页
iOS 高级开发(1)之 Runtime 消息转发

iOS 高级开发(1)之 Runtime 消息转发

作者: iOS刘耀宗 | 来源:发表于2021-08-15 00:20 被阅读0次

作为一个 iOS 开发者. 尤其是想向高级以及资深工程师前进的程序员. 接下来小刘发的一系列的文章都是需要掌握的东西. 你可能认为很多东西平时开发用不到, 那么请想想你和初中级程序员的区别是什么? 只有掌握原理才能在技能上有更深一步的追求. 关注我, 一起向资深iOS 开发进军.
接下来小刘将发布一系列关于 iOS 高级开发的一系列文章, 希望能和大家一起讨论,学习进步!
Demo 下载
runtime 消息转发
我们常用的对象调用方法的本质,实际是向这个对象发送一条消息.其本质是调用 c 语言的 api.
例如

//方法调用
    [[Dog new] sendMessage:@"hello"];
    //上面的代码等同于下面的代码
    objc_msgSend([Dog new],@selector(sendMessage:),@"hello");

如果上面的Dog 类 只在.h 文件声明方法, 不在.m 文件实现. 那么我们就会遇见常见的崩溃

'-[Dog sendMessage:]: unrecognized selector sent to instance 0x600002d901d0'

那么我们为什么要认识消息转发机制呢, 它能帮助我们干什么呢. 消息转发机制的步骤又是什么?

消息转发机制的步骤可以分为三个步骤:
1.动态方法解析
2.快速转发
3.慢速转发

1.动态解析:

    [[Dog new] sendMessage:@"hello"];

调用了上面的代码实际上会触发当前类的

+ (BOOL)resolveInstanceMethod:(SEL)sel

如果是类方法则会触发

+ (BOOL)resolveClassMethod:(SEL)sel

在动态解析方法的时候,首先要匹配方法的名字. 如果匹配成功, 则会动态添加为此类添加一个方法

//动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    //1.先要匹配方法 获取方法名称
    NSString *methodName = NSStringFromSelector(sel);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        /*
         Class cls: 将要给添加方法的类,传的类型 [类名  class]
         SEL name: 将要添加的方法名,传的类型   @selector(方法名)
         IMP imp:实现这个方法的函数 ,传的类型   1,C语言写法:(IMP)方法名    2,OC的写法:class_getMethodImplementation(self,@selector(方法名:))
         const char *types:表示我们要添加的方法的返回值和参数
         "v@:@":v:是添加方法无返回值    @表示是id(也就是要添加的类) :表示添加的方法类型  @表示:参数类型
         */
        return  class_addMethod(self, sel, (IMP)sendMessage, "V@:@");
    }
    return  NO;
}

如果此时 sendMessage 未实现,找不到这个函数. 那么就会崩溃

如果匹配不到方法, 那么返回 NO. 那么则会进入第二个步骤
2.快速转发阶段

//快速转发  找到一个备用的接受者来接收它,如果没有找到还是走它自己默认的步骤
-(id)forwardingTargetForSelector:(SEL)aSelector
{
    //1.先要匹配方法 获取方法名称
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        //如果 Cat 类实现了 sendMessage:的方法,则会调用 Cat 的方法
        return  [Cat new];
    }
    return  [super forwardingTargetForSelector:aSelector];
}

如果快速转发没有找到备用接收者.那么就进入第三步骤
3.慢速转发. 慢速转发又分为两个小步骤
3.1: 方法签名 把当前方法的具体信息保存起来

//方法签名
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    //1.先要匹配方法 获取方法名称
    NSString *methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        //利用下面的方法进行方法签名
        return  [NSMethodSignature signatureWithObjCTypes:"V@:@"];
    }
    //继续按照默认步骤走, 进入最后慢速阶段的消息转发
    return  [super methodSignatureForSelector:aSelector];
    
}
//消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = [anInvocation selector];
    //找到一个备用者
    Cat *cat = [Cat new];
    //是否能响应方法
    if ([cat respondsToSelector:sel]) {
        //把当前的方法指给这个对象
        [anInvocation invokeWithTarget:cat];
    } else {
        //继续走对应默认的步骤  指向崩溃方法
        [super forwardInvocation:anInvocation];
    }
}

走到最后还是没有找到消息转发者. 那么就进入一个提供报错信息的方法

//提供报错信息  重写这个方法可以防止崩溃哦
-(void)doesNotRecognizeSelector:(SEL)aSelector
{
    NSLog(@"不会崩溃哦");
}

以上便是整个消息转发的流程.

那么消息转发可以帮助我们干什么呢?
我们可以在消息转发的流程里面添加代码,防止崩溃. 也可以根据自己的业务需求,在对应的阶段修改逻辑,达到我们想要的业务逻辑.

以上便是我通过多方面的学习对消息转发的理解,有问题,可以留言沟通哦

相关文章

网友评论

      本文标题:iOS 高级开发(1)之 Runtime 消息转发

      本文链接:https://www.haomeiwen.com/subject/tpdybltx.html