Runtime消息转发机制

@interface ViewController ()

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *age;

@end

@implementation ViewController

// 告诉编译器,不要生成set 和 get方法,但是调用get 和set也不会出错
@dynamic name;

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.name = @"pangshishan";
}

上述代码中中name是一个属性,在@implementation中,用@dynamic修饰之后,name属性将不会生成getter 和 setter方法。在下面这段代码中,用setter方法给name复制,系统会抛出“ -[ViewController setName:]: unrecognized selector sent to instance 0x7f95f2f07100”异常,这是由于系统找不到setName:这个方法。用runtime消息转发,有三次机会可以不让系统抛出这个异常(当然,把这个方法写上肯定是没问题的,但我们今天主题是:Runtime的转发机制)


我们在SomeOtherObject的 .m中实现setName:方法,让SomeOtherObject接受未处理的消息


#pragma mark - 1.重定向方法,在本类里进行方法重定向处理
void dynamicMethodIMP(id self, SEL _cmd)
{
    NSLog(@"dynamicMethodIMP -- %@", NSStringFromSelector(_cmd));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"sel is %@", NSStringFromSelector(sel));
    // 当方法是setter方法时
    if (sel == @selector(setName:)) {
        // 动态给本类添加方法
        class_addMethod([SomeOtherObject class], sel, (IMP)dynamicMethodIMP, "v@:");
    }

    return [super resolveInstanceMethod:sel];
}

#pragma mark - 2. 找个类处理她
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if ([NSStringFromSelector(aSelector) isEqualToString:@"setName:"]) {
        return [[SomeOtherObject alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

#pragma mark - 3. 最后一步,把selector的所有细节包程NSInvocation, invocation对象负责分发消息给其他对象,这一步是完整的转发机制
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    // (1)把2注释掉,1解开注释,依然有效
//    if ([NSStringFromSelector(aSelector) isEqualToString:@"setName:"]) {
//        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
//    }
//    return [super methodSignatureForSelector:aSelector];
    // (2)
    NSMethodSignature *signature = [super methodSignatureForSelector: aSelector];
    if (signature == nil) {
        if ([SomeOtherObject instancesRespondToSelector:aSelector]) {
            signature = [SomeOtherObject instanceMethodSignatureForSelector:aSelector];
        }
    }
    return signature; // 只有当返回值不是空的时候才能转发成功
}
// anInvocation里打包了此类不能处理的selector所有的细节
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    // 将anInvocation里打包的消息,发送给SomeOtherObject处理,如果SomeOtherObject也不能处理,可以在它里面继续转发
    // 这也是变相的实现了多继承,但是消息转发只是将一部分功能放到其他类,并没有多继承造成的代码过于丰富的问题
    [anInvocation invokeWithTarget:[[SomeOtherObject alloc] init]];
}

以上简单的介绍了一下Runtime的消息转发机制,这是我写的demo,感兴趣的小伙伴可以下载

点击这里查看demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值