iOS中消息转发机制及工程应用

本文是按照博客点击打开链接学习runtime,整理备份,方便温故知新。

iOS的动态性主要体现在runtime中,即在程序运行阶段可以调用任意方法,移花接木等黑科技,各种调用。

关于消息机制,先补充几点:

1.调用方法的本质是调用objc_msgSend函数发送消息。在发送消息途中可以任意转发,截留消息,实现上述的移花接木之类的黑科技。

2.在正常情况下,如果调用的函数都是实现好的,则不用考虑消息转发,只需要按照调用类->父类->父类的顺序挨着找就行了。如果一直都没有找到,则需要考虑消息转发。如果不做转发处理,则会直接cash

3.消息转发主要是三级处理,每一级我们都可以单独处理,处理好了就可以在没找到函数的情况下处理事件,防止cash(找不到指的是在object的methodList中找不到)。三级分别是,动态方法解析,备用接收者,完整转发,如下图:

少说废话,直接上三个消息转发的demo

1.动态方法解析

废话:在收到异常消息的时候,动态的添加c方法,并实现异常消息处理,如下

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    //从系统里匹配SEL,如果没有就注册SEL
    SEL systemSel = sel_registerName(sel_getName(sel));
    //把所有未知的SEL指向dynamicMethodIMP的实现,让其帮忙打印错误信息
    class_addMethod(self, systemSel, (IMP)dynamicMethodIMP, "v@:");
    BOOL boolValue = [super resolveClassMethod:systemSel];
    return boolValue;
}

void dynamicMethodIMP(id self, SEL _cmd){
    NSString * className = NSStringFromClass([self class]);
    NSString * selName   = NSStringFromSelector(_cmd);
    NSLog(@"%@:不是%@的方法",className,selName);
}

2.备用接收者

废话:当前target是找不到方法了,可以推给备胎,让备胎去处理。这样处理的话方法调用者并不知道是其他对象处理的,很隐蔽,利于封装。而且容易实现,代码量少。

- (id)forwardingTargetForSelector:(SEL)aSelector{
    return [[Girl alloc] init];
}

3.完整消息转发

废话:其实这也是找其他对象接受消息,相比2更任意一些。而且还可以集中转移到某个类,可以让该类实现类似于多继承的效果,excited!

//在这里产生方法签名,以确保NSInvocation能被转发的girl类执行,不然的话会出现错误
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSMethodSignature * signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {
        if ([Girl instancesRespondToSelector:aSelector]) {
            signature = [Girl instanceMethodSignatureForSelector:aSelector];
        }
    }
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    if ([Girl instancesRespondToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:[[Girl alloc]init]];
    }
}


总结:
在一个函数找不到时,OC提供了三种方式去补救:
1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数
2、调用forwardingTargetForSelector让别的对象去执行这个函数
3、调用forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。

如果都不中,调用doesNotRecognizeSelector抛出异常。

疑问

Q1:那我们只用最后一个接盘侠方法多好啊,为什么还需要前2个呢?
其实还与这3个方法的用途不同有关:
运行期添加方法,用1;
转发给另1个对象、改变方法时,用2;
需要转发给多个对象时,用3;

而且,步骤越往后,处理消息的代价越大,到最后一个阶段时,都创建了 NSInvocation 对象了。

Q2:消息转发有哪些应用场景呢?
可以在运行期再加入某方法,例如 Teacher 类里有teach方法,DrugDealer 类里有letsCook方法,通过一号接盘侠方法,我们可以在运行期把 saleDrug 偷摸加到 teacher 的方法列表中,让 teacher 具备贩毒的功能,[teacher guessWhatHeDo],实际调用的是[teacher letsCook],唉呀妈呀,绝命毒师啊。

把方法转给其他对象处理,再举个例子,还是 Teacher 类(博主跟老师有仇吗...),[teacher letsCook],可以把对象在运行期换为drugDealer。再来一个 Cook 类,也有 letsCook 方法,但这次这方法不是 cook 毒品,而是 cook 菜。因此既可以通过[teacher letsCook] 实现[drugDealer letsCook],也可以实现[cook letsCook]。相当于 OC 实现了多重继承,虽然有点不太恰当...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值