• iOS 消息转发


    当我们调用一个不存在的方法时,就会报:unrecognized selector sent to instance **。消息接收者找不到对应的selector,这样就启动了消息转发机制,我们可以通过代码在消息转发的过程中告诉对象应该如何处理未知的消息,默认抛出上面的异常。

    1、对象在收到未知消息后,首先会调用 + (BOOL)resolveInstanceMethod:(SEL)sel 或者 + (BOOL)resolveClassMethod:(SEL)sel ,询问是否有动态方法来进行处理

    // 调用
    Person *p1 = [[Person alloc] init];
    [p1 performSelector:@selector(speak)];
    [Person performSelector:@selector(eat)];
    // Person.m
    #import <objc/runtime.h>
    void speak(id self,SEL _cmd) {
        NSLog(@"Now I can speak");
    }
    void eat(id self,SEL _cmd) {
        NSLog(@"eat......");
    }
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        NSLog(@"resolveInstanceMethod:%@",NSStringFromSelector(sel));
        if (sel == @selector(speak)) {
            class_addMethod([self class], sel, (IMP)speak, "vvv");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    + (BOOL)resolveClassMethod:(SEL)sel
    {
        NSLog(@"resolveClassMethod:%@",NSStringFromSelector(sel));
        if (sel == @selector(eat)) {
            class_addMethod([self superclass], sel, (IMP)eat, "vvv");
            return YES;
        }
        return [super resolveClassMethod:sel];
    }
    添加动态方法

     当Person收到未知消息的时候,如果是实例方法会调用resolveInstanceMethod方法、类方法会调用resolveClassMethod方法,然后通过class_addMethod方法动态添加实现方法来解决未知消息,此时消息转发过程提前结束

    2、如果第一步返回的是NO,也就是没有动态新增实现方法就会调用第二步。使用 - (id)forwardingTargetForSelector:(SEL)aSelector 让别的类帮忙处理。

    //  调用
    //  ((void (*)(id, SEL))objc_msgSend)(p1, @selector(fly));
    [p1 performSelector:@selector(fly)];
    // Person.m
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        NSLog(@"forwardingTargetForSelector:  %@", NSStringFromSelector(aSelector));
        Bird *bird = [[Bird alloc] init];
        if ([bird respondsToSelector: aSelector]) {
            return bird;
        }
        return [super forwardingTargetForSelector: aSelector];
    }
    // Bird.m
    - (void)fly
    {
        NSLog(@"I can fly");
    }
    让其他类处理这条消息

    3、如果前两种方法都不能处理未知消息,则使用下面方法

    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        NSLog(@"forwardInvocation: %@", NSStringFromSelector([anInvocation selector]));
        if ([anInvocation selector] == @selector(drink)) {
            Bird *b1 = [[Bird alloc] init];
            [anInvocation invokeWithTarget:b1];
        }
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        NSLog(@"method signature for selector: %@", NSStringFromSelector(aSelector));
        if(aSelector == @selector(drink)) {
            return [NSMethodSignature signatureWithObjCTypes:"aaaa"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    最后一步

    如果最后消息还是未能处理,还会调用 - (void)doesNotRecognizeSelector:(SEL)aSelector ,可以在这个方法里做些处理,防止crash

    未知消息处理顺序

  • 相关阅读:
    Collections类
    delphi Tdxribbon 设置背景和skin 皮肤
    模拟一个带背景的 TPanel
    将控件画成圆角的效果(Delphi)
    Delphi自写组件:可设置颜色的按钮
    delphi StringGrid 表格的复制粘贴
    学习人工智能
    python 环境搭建和Spyder的安装应用
    python 廖雪峰的官方网站
    spyder 安装
  • 原文地址:https://www.cnblogs.com/chenyanliang/p/9299331.html
Copyright © 2020-2023  润新知