Swift进阶iOS Developer编程知识点

iOS 消息转发机制(VN的逃生之路)

2017-03-31  本文已影响705人  丨n水瓶座菜虫灬

故事背景:在德玛西亚的战场上,硝烟弥漫,紫色方英雄薇恩正在河道处戏弄一只毫无攻击性的螃蟹,丝毫没有感觉到附近的杀气。突然,从草丛中冒出敌方四员大将,只听其中一名怒吼:“德玛西亚!!!”......

描述此故事前,先附上一张故事的整体流程图:

消息转发.png

这里有一个类ADCHero, 有四个方法,分别是skillQ, skillW, skillE, skillR, 但是skillR方法没有实现。
在ADCHero.h 文件

@interface ADCHero : NSObject

// Q技能
- (void)skillQ;

// W技能
- (void)skillW;

// E技能
- (void)skillE;

// R技能
- (void)skillR;

@end

在ADCHero.m文件

@implementation ADCHero

- (void)skillQ {
    NSLog(@"ADC 发起了Q技能");
}

- (void)skillW {
    NSLog(@"ADC 发起了W技能");
}

- (void)skillE {
    NSLog(@"ADC 发起了E技能");
}

@end 

创建一个薇恩对象

 ADCHero *vn = [[ADCHero alloc] init];

薇恩在面临生命危险的时候,准备逃跑,于是准备开启大招R 进入隐身状态。

调用方法

[vn skillR];// 直接运行程序,报错: unrecognized selector sent to instance 0x170006e00 程序崩溃,因为找不到方法实现。

在Objective-C中对象调用方法,实际上是给对象发送消息,在底层会调用objc_msgSend方法

//  第一个参数是消息的接收者; 第二个参数是要调用的方法名; 后面的参数依次是调用的方法中的参数。
objc_msgSend(receiver, selector, arg1, arg2, …) :

结局一: 可是薇恩忘了自己没有加R的技能点,使用不出R技能,于是在敌方四人的围殴下,壮烈牺牲。(程序崩溃)

打印信息:

程序崩溃

为了不让薇恩这么快就死了,Objective-C做了一些处理(消息转发)

在薇恩没法使用R技能时,先询问薇恩,能否使用其它的技能逃跑。薇恩想:“我没有R技能,那我直接闪现逃跑吧”。情形如下。

此时会调用ADCHero类的类方法resolveInstanceMethod, 在这个方法中动态添加其它的方法。

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"%s", __func__);
    NSString *selectorString = NSStringFromSelector(sel);
    if ([selectorString isEqualToString:@"skillR"]) { // 如果方法名是skillR
        class_addMethod(self, sel, (IMP)skillFlash, "@:"); // 动态添加方法skillFlash, 参数一: 消息接收者;参数二: 调用的方法名;参数三:方法对应的实现地址;参数四: 类型编码。
        return YES; // 
    }

    return [super resolveInstanceMethod:sel];
}

void skillFlash() {
    NSLog(@"闪现");
}

打印信息:

结果

薇恩使用闪现极限逃生。。。 但是,故事的情节有变化,有的时候闪现是处于CD状态,也无法使用,不能够闪现逃走,vn在想:"要不找队友支援吧!",于是把消息发给了队友日女。正巧日女从附近焦急地赶过来。

在代码中,将resolveInstanceMethod的方法内部更改一下。并重写forwardingTargetForSelector方法, forwardingTargetForSelector方法内部,创建了一个辅助英雄日女,该类中有方法skillR ,并且已经实现。
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%s", func);
return NO;
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"%s", __func__);
    NSString *selectorString = NSStringFromSelector(aSelector);
    if ([selectorString isEqualToString:@"skillR"]) {
        AsistantHero *rinv = [[AsistantHero alloc] init];
        return rinv;
    }

    // 如果队友不在身边
    return [super forwardingTargetForSelector:aSelector];
}

AsistantHero类.h .m文件如下:

 // AsistantHero.h文件
@interface AsistantHero : NSObject

// Q技能
- (void)skillQ;

// W技能
- (void)skillW;

// E技能
- (void)skillE;

// R技能
- (void)skillR;

@end

// AsistantHero.m 文件
@implementation AsistantHero
- (void)skillQ {
    NSLog(@"SUP 发起了Q技能");
}

- (void)skillW {
    NSLog(@"SUP 发起了W技能");
}

- (void)skillE {
    NSLog(@"SUP 发起了E技能");
}

- (void)skillR {
    NSLog(@"SUP 发起了R技能 保护了ADC");
}

@end

运行程序,打印信息如下:

打印信息

日女使用R技能减速了敌方四人,并救援薇恩 顺利逃走。。。。。。但是,故事的情节又有变化,由于在下路对线的时候ADC薇恩和辅助日女产生了分歧,导致日女心里不太爽,于是日女不大想救薇恩。结果薇恩又死了。

在代码中,将forwardingTargetForSelector方法内部修改一下:

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"%s", __func__);
    return [super forwardingTargetForSelector:aSelector];
}

运行程序,打印信息如下:

打印信息

然而,故事情节又有转机,薇恩告诉日女:“如果你不救我,我就挂机!”,日女考虑到这把是自己的晋级赛,于是心想:“就救他一次吧,反正救人一命胜造七级浮屠。”

在代码中, 重写methodSignatureForSelector方法和forwardInvocation方法.

// 完整的消息转发机制
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSLog(@"%s", __func__);
    NSString *selectorString = NSStringFromSelector(aSelector);
    if ([selectorString isEqualToString:@"skillR"]) {
        NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
        return signature;
    }

    return nil;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"%s", __func__);
    AsistantHero *rinv = [[AsistantHero alloc] init];
    if ([rinv respondsToSelector:[anInvocation selector]]) {
        [anInvocation invokeWithTarget:rinv];
    } else {
        [super forwardInvocation:anInvocation];
    }
}

运行程序,结果如下:

打印结果

最后日女抓住了最后一次机会,救下了薇恩,薇恩为了表达感激之情,让他的王者表哥带日女打赢了晋级赛。

总结一下,在上面的故事中,薇恩面对敌方四人埋伏,自己又没有R技能逃亡。如何使用iOS的消息转发机制一步一步的惊险逃脱。当他调用 [vn skillR]方法时,究竟做了哪些事。
第一阶段,看自己能不能在接受到这个消息时,使用闪现逃跑,如果不能,进入到第二个阶段。
第二阶段,看有没有辅助在身旁替自己接受这个消息,如果有就把消息传递给辅助,让辅助救援他。如果没有,则进入第三个阶段。
第三阶段,把消息封装起来,告诉辅助,给他最后一次机会,让他设法处理。

对应与消息转发机制,iOS消息转发分为三大阶段:

参考资料:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html
https://hit-alibaba.github.io/interview/iOS/ObjC-Basic/Runtime.html

上一篇下一篇

猜你喜欢

热点阅读