美文网首页面试精品iOS开发
iOS - 动态添加方法和消息转发

iOS - 动态添加方法和消息转发

作者: 壮骨 | 来源:发表于2017-12-15 16:20 被阅读12次
假设

假设,声明一个 TestObect 的类,.h中声明了 test方法,但是.m中未实现,但是调用了这个方法会报一个经典的错误

没有找到方法实现

ok,前提已经说完了,我们就从找这个错误原因讲起。

解决

首先,该方法在调用时,系统会查看这个对象能否接收这个消息(查看这个类有没有这个方法,或者有没有实现这个方法。),如果不能并且只在不能的情况下,就会调用下面这几个方法,给你“补救”的机会,你可以先理解为几套防止程序crash的备选方案,我们就是利用这几个方案进行消息转发,注意一点,前一套方案实现后一套方法就不会执行。如果这几套方案你都没有做处理,那么程序就会报错crash。

方案一:
+ (BOOL)resolveInstanceMethod:(SEL)sel;
方案二:
- (id)forwardingTargetForSelector:(SEL)aSelector;
方案三:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;

抽象解释一下消息转发: 比赛足球时,脚下有球的那名球员,如果他的位置不利于射门或者他的球即将被对方球员抢断,这时最好是把球传出去,这里的球就相当于消息。

1.resolveInstanceMethod

当你调用方法时,系统会调用 resolveInstanceMethod 这个方法,参数SEL就是你调用的方法,那么我们可以重写这个方法,从而实现动态添加一个方法,来使程序不会崩溃.

// 需要导入 #import <objc/runtime.h>
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(test)) {        
        IMP imp = class_getMethodImplementation(self, @selector(run));
        class_addMethod([self class], sel, imp, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
动态添加方法
科普一下
  1. IMP获取
    如果添加方法实现是用OC风格写的 那么用 class_getMethodImplementation 来获取IMP
    如果是用C风格写的函数
void run(id self, SEL _cmd) {
    NSLog(@" %@ %s", sel_getName(_cmd));
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        class_addMethod([self class], sel, (IMP)run, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
  1. class_addMethod 参数
  • 参数一: Class:我们需要一个class,比如我的[self class]。

  • 参数二: SEL:方法

  • 参数三: IMP :IMP就是Implementation的缩写,它是指向一个方法实现的指针,每一个方法都有一个对应的IMP。这里需要的是IMP,所以你不能直接写方法,参照上述 1 来获取

  • 参数四: const char *types:
    ”v@:”意思就是这已是一个void类型的方法,没有参数传入。
    “i@:”就是说这是一个int类型的方法,没有参数传入。
    ”i@:@”就是说这是一个int类型的方法,有一个对象参数传入。
    因为每一个方法会默认隐藏两个参数,self、_cmd,self代表方法调用者,_cmd代表这个方法的SEL,该参数就是用来描述这个方法的返回值、参数的,v 代表返回值为void,@ 表示self,: 表示_cmd。
    详见官网

  • 注意: 用这个方法添加的方法是无法直接调用的,必须用performSelector:调用。
    因为performSelector是运行时系统负责去找方法的,在编译时候不做任何校验;如果直接调用编译是会自动校验。 然后class_addMethod添加方法是在运行时添加的,你在编译的时候还没有这个本类方法,所以当然不行啦。

2.forwardingTargetForSelector

forwardingTargetForSelector 是NSObject的函数,来决定谁执行方法

TestObect.m
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    return [super resolveInstanceMethod:sel];
}

- (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(test)) {
        return [ForwardTestObect new];
    }
    return [super forwardingTargetForSelector:aSelector];
}

ForwardTestObect.m
- (void)test{
    NSLog(@"%@  --- test ---",self);
}
打印截图
3.methodSignatureForSelector和forwardInvocation

methodSignatureForSelector用来生成方法签名,这个签名就是给forwardInvocation中的参数NSInvocation调用的。
开头我们要找的错误unrecognized selector sent to instance原因,原来就是因为methodSignatureForSelector这个方法中,由于没有找到run对应的实现方法,所以返回了一个空的方法签名,最终导致程序报错崩溃。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    SEL sel = [anInvocation selector];
    //创建接受对象
    ForwardTestObect *forward = [ForwardTestObect new];
    //判断是否实现了这个方法
    if ([forward respondsToSelector:sel]) {
        // 唤醒这个方法
        [anInvocation invokeWithTarget:forward];
    }else{
        [super forwardInvocation:anInvocation];
    }
}
打印截图

其中: [NSMethodSignature signatureWithObjCTypes:"v@:"]; 方法的参数参考上述 class_addMethod 方法的参数四
forwardInvocation: 方法就是一个不能识别消息的分发中心,将这些不能识别的消息转发给不同的接收对象,或者转发给同一个对象,再或者将消息翻译成另外的消息,亦或者简单的“吃掉”某些消息,因此没有响应也不会报错。这一切都取决于方法的具体实现。
注意:forwardInvocation:方法只有在消息接收对象中无法正常响应消息时才会被调用。所以,如果我们向往一个对象将一个消息转发给其他对象时,要确保这个对象不能有该消息的所对应的方法。否则,forwardInvocation:将不可能被调用。

最后

1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数
2、调用forwardingTargetForSelector让别的对象去执行这个函数
3、调用methodSignatureForSelector(函数符号制造器)和forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。
如果都不中,调用doesNotRecognizeSelector抛出异常。

方案1是动态添加方法,方案2和方案3 就是OC的消息转发机制

相关文章

  • iOS中的消息转发机制

    OC由于运行时特性,可以在运行期间动态添加方法,这个寻找动态添加的方法的过程就是动态消息转发。iOS的消息转发机制...

  • 消息转发机制

    OC由于运行时特性,可以在运行期间动态添加方法,这个寻找动态添加的方法的过程就是动态消息转发。 iOS的消息转发机...

  • iOS - 动态添加方法和消息转发

    假设 假设,声明一个 TestObect 的类,.h中声明了 test方法,但是.m中未实现,但是调用了这个方法会...

  • MG--iOS 消息机制

    msgSend 消息发送 动态方法解析 动态添加方法image.png 消息转发

  • OC中的消息转发过程

    消息转发 描述:如果类不能执行这个方法,会执行动态消息转发,如果该类还是不能动态的添加方法,则走完整的消息转发。分...

  • Runtime

    相关简单介绍 消息机制消息传递机制消息转发机制-动态添加方法消息转发机制-快速转发消息转发机制-慢速转发消息转发机...

  • Aspcet

    Aspect tips: 未识别消息转发流程: 1.resolveInstanceMethod (给类动态添加方法...

  • IOS 动态方法解析和消息转发

      好的!今天我们来练习IOS,当消息发送给没有实现该消息方法的对象时,会开始消息转发流程:动态方法解析->寻找备...

  • 消息转发机制

    当一个对象调用 1、动态添加方法:resloveInstanceMethod 2、转发消息:forwardingT...

  • unrecognized selector sent to in

    消息转发以及动态解析方法 消息转发机制基本上分为三个步骤: 动态方法解析 备用接收者 完整转发 首先,对于动态方法...

网友评论

    本文标题:iOS - 动态添加方法和消息转发

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