消息转发机制(Effective Objective-C摘录学习篇)

对象在收到无法解读的消息之后会发生什么情况

消息转发分为两大阶段。第一阶段先征询接收者,所属的的类,看其是否能动态添加方法,以处理当前这个“未知的选择子”,这叫做“动态方法解析”。第二阶段涉及“完整的的消息转发机制”。这又细分为两小步。首先,请接收者看看有没有其它对象能处理这条消息。若有,则运行期系统会把消息转给那个对象,于是消息转发过程结束,一切如常。若没有“备援接收者”则启动完整的消息转发机制,运行期系统会把与消息有关的全部细节都封装到NSInvocation对象中,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。

动态方法解析

对象在收到无法解读的消息后,首先将调用其所属类的下列类方法

+ (Bool)resolveInstanceMethod:(SEL)selector

该方法的参数就是那个未知的选择子,其返回值为Boolean类型,表示这个类是否能新增一个实例方法用以处理此选择子。假如尚未实现的方法不是实例方法而是类方法,那么运行期系统就会调用另外一个方法,该方法与“resolveInstanceMethod:”类似,叫做“resolveClassMethod:”
使用这种方法的前提是:相关方法的实现代码已经写好,只等着运行的时候动态插在类里面就可以了。此方案常用来实现@dynamic属性。

备援接收者

当前接收者还有第二次机会能处理未知的选择子,在这一步中,运行期系统会问它:能不能把这条消息转发给其它接收者来处理。与该步骤对应的处理方法如下:

- (id)forwardingTargetForSelector:(SEL)selector

方法参数代表未知的选择子,若当前接收者能找到备援对象,则将其返回,若找不到,就返回nil。我们可以用“组合”来模拟出“多重继承”的某些特性。在一个对象内部,可能还有一系列其它对象,该对象可经由此方法将能够处理某选择子的相关对象返回,这样的话,在外界看来,好像是该对象亲自处理了这些消息似的。

完整的消息转发

如果转发算法已经来到这一步的话,那么唯一能做的就是启用完整的消息转发机制了。首先创创建NSInvocation对象,把与尚未处理的那条消息有关的全部细节都封装于其中。此对象包含选择子、目标及参数。在触发NSInvocation对象时,“消息派发系统”将亲自出马,把消息指派给目标对象。
此步骤会调用下列方法来转发消息:
– (void)forwardInvocation:(NSInvocation *)invocation
实现此方法时,若发现调用操作不应由本类来处理,则需要调用超类的同名方法。这样的话,继承体系中的每个类都有机会处理此调用请求,直至NSObject。如果最后调用了NSObject的方法,那么该方法还会继而调用“doesNotRecognizeSelector:”以抛出异常,此异常表明选择子最终未能得到处理。

完整的例子演示动态方法解析

以动态方法解析@dynamic属性,类自动处理相关属性的存放与获取操作(编写一个类似“字典”的对象)

@interface EOCAutoDictionary : NSObject

@property (nonatomic, strong) NSString *string;
@property (nonatomic, strong) NSNumber *number;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) id opaqueObject;

@end
//需要引入头文件#import <objc/runtime.h>
@interface EOCAutoDictionary ()

@property (nonatomic, strong) NSMutableDictionary *backingStore;

@end

@implementation EOCAutoDictionary

@dynamic string, number, date, opaqueObject;

- (instancetype)init {
    if (self = [super init]) {
        _backingStore = [NSMutableDictionary new];
    }
    return self;
}


id autoDictionaryGetter(id self, SEL _cmd) {
    EOCAutoDictionary *typedSelf = (EOCAutoDictionary *)self;
    NSMutableDictionary *backingStore = typedSelf.backingStore;
    NSString *key = NSStringFromSelector(_cmd);
    return [backingStore objectForKey:key];
}

void autoDictionarySetter(id self, SEL _cmd, id value) {
    EOCAutoDictionary *typedSelf = (EOCAutoDictionary *)self;
    NSMutableDictionary *backingStore = typedSelf.backingStore;
    NSString *selectorString = NSStringFromSelector(_cmd);
    NSMutableString *key = [selectorString mutableCopy];
    [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];
    [key deleteCharactersInRange:NSMakeRange(0, 3)];
    NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar];
    if (value) {
        [backingStore setObject:value forKey:key];
    } else {
        [backingStore removeObjectForKey:key];
    }
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString *selectorString = NSStringFromSelector(sel);
    if ([selectorString hasPrefix:@"set"]) {
        class_addMethod(self, sel, (IMP)autoDictionarySetter, "v@:@");
    } else {
        class_addMethod(self, sel, (IMP)autoDictionaryGetter, "@@:");
    }
    return YES;
}

@end
    原文作者:NexTOne
    原文地址: https://www.jianshu.com/p/ad3ff549f242
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞