前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对象、消息、运行期--12:runtime消息转发

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

作者头像
xy_ss
发布2023-11-22 08:20:31
1260
发布2023-11-22 08:20:31
举报
文章被收录于专栏:浮躁的喧嚣浮躁的喧嚣

消息转发

消息转发分为俩大阶段

  • 动态方法解析
  • 完整的消息转发机制
  • 消息转发全流程:
    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个有效方法

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-07-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 消息转发
  • 动态方法解析(动态添加方法)
  • 完整的消息转发机制
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档