消息转发机制
来源:互联网 发布:dts音效软件 编辑:程序博客网 时间:2024/04/30 10:19
本文主要摘自《Effective Objective-C》
当对象收到无法解读的消息后,会启动“消息转发”(message forwarding)机制。
消息转发分为三步:1. 动态方法解析
2. 备援接受者
2. 完整的消息转发机制
动态方法解析
对象在收到无法解读的消息后,首先调用下列方法:
+ (BOOL)resolveClassMethod:(SEL)sel+ (BOOL)resolveInstanceMethod:(SEL)sel
参数是位置的选择子(selector),返回值表示这个类是否能新增一个方法用以处理这个选择子。使用动态方法解析需要相关代码已经写好,只等运行时动态插在类中。此方法常用语实现@dynamic属性。
// DLMsgForwardingDemo.h#import <Foundation/Foundation.h>extern NSString * const kRemoveAllObjectsMethod;extern NSString * const kClearAllObjectsMethod;@interface DLMsgForwardingDemo : NSObject@property (nonatomic, copy) NSString *dynamicStr;@end// DLMsgForwardingDemo.m#import "DLMsgForwardingDemo.h"#import <objc/runtime.h>NSString * const kRemoveAllObjectsMethod = @"removeAllObjects";NSString * const kClearAllObjectsMethod = @"clearAllObjects";@interface DLMsgForwardingDemo ()@property (nonatomic, strong) NSMutableDictionary *backingStore;@end@implementation DLMsgForwardingDemo@dynamic dynamicStr;+ (BOOL)resolveInstanceMethod:(SEL)sel { NSString *selectorString = NSStringFromSelector(sel); if ([selectorString isEqualToString:kRemoveAllObjectsMethod] || [selectorString isEqualToString:kClearAllObjectsMethod]) { return [super resolveInstanceMethod:sel]; } if ([selectorString hasPrefix:@"set"]) { class_addMethod(self, sel, class_getMethodImplementation(self, @selector(autoDictionarySetter:)), "v@:@"); return YES; } else { class_addMethod(self, sel, class_getMethodImplementation(self, @selector(autoDictionaryGetter)), "@@:"); return YES; } return [super resolveInstanceMethod:sel];}- (instancetype)init { if (self = [super init]) { _backingStore = [NSMutableDictionary new]; } return self;}- (id) autoDictionaryGetter { NSString *key = NSStringFromSelector(_cmd); return _backingStore[key];}- (void)autoDictionarySetter: (id)value { NSString *selectorString = NSStringFromSelector(_cmd); NSMutableString *key = [selectorString mutableCopy]; [key deleteCharactersInRange:NSMakeRange(0, 3)]; [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)]; NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString]; [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar]; if (value) { _backingStore[key] = value; } else { [_backingStore removeObjectForKey:key]; } }
备援接受者
当前接收者还有第二次机会能处理未知的选择子,系统会询问:能不能把这条消息转给其他接受者来处理。调用方法如下:- (id)forwardingTargetForSelector:(SEL)aSelector
方法参数是未知的选择子,若当前接受者能找到备援接受者,则将其返回;若找不到则返回nil。通过此方案,我们可以用组合(composition)来模拟出“多继承”(multiple inheritance)的某些特性。在一个对象内部,可能还有一系列其他对象,该对象可经由此方法将能够处理某选择子的相关内部对象返回,这样的话,在外界看来,好像是该对象亲自处理了这些消息。
请注意,我们无法操作经由这一步所转发的消息。若是想在发送给备援接受者之前先修改消息内容,那就得通过完整的消息转发机制来做了。
- (id)forwardingTargetForSelector:(SEL)aSelector { NSString *selectorString = NSStringFromSelector(aSelector); if ([selectorString isEqualToString:kRemoveAllObjectsMethod] && [_backingStore respondsToSelector:aSelector]) { return _backingStore; } return [super forwardingTargetForSelector:aSelector];}
完整的消息转发
如果转发算法已经到这一步,那么唯一能做的就是启动完整的消息转发机制。首先创建NSInvocation对象,把与尚未处理的那条消息有关的全部细节风雨其中。此对象包含选择子,对象(target)及参数。在触发NSInvocation对象时,“消息派发系统”(message-dispatch system)将亲自出马,把消息指派给目标对象。此步骤会调用下列方法来转发消息:
- (void)forwardInvocation:(NSInvocation *)anInvocation
在触发消息前,先以某种方式改变消息内容,比如追加另一个参数,或更换选择子等等。在这里说一下,要使用这个方法,必须override另外一个方法:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
这个方法用来返回给定选择子的签名。在调用forwardInvocation之前会调用此方法获取签名。- (void)forwardInvocation:(NSInvocation *)anInvocation { NSString *selectorString = NSStringFromSelector(anInvocation.selector); if ([selectorString isEqualToString:kClearAllObjectsMethod]) { anInvocation.selector = NSSelectorFromString(kRemoveAllObjectsMethod); [anInvocation invokeWithTarget:_backingStore]; } else { [super forwardInvocation:anInvocation]; }}- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature* signature = [super methodSignatureForSelector:aSelector]; if (!signature) { NSString *selectorString = NSStringFromSelector(aSelector); if ([selectorString isEqualToString:kClearAllObjectsMethod]) { signature = [NSMethodSignature signatureWithObjCTypes:"v@:"]; } } return signature;}
消息转发全流程
接受者在每一步中均有机会处理消息。步骤越往后,处理消息的代价越大。最好能在第一步就处理完,这样的话,运行期系统可以将此方法缓存起来。如果这个累的实例稍后收到同名选择子,那么根本无需启动消息转发流程。而第三步需要创建并处理完整的NSInvocation。
DLMsgForwardingDemo *obj = [DLMsgForwardingDemo new]; obj.dynamicStr = @"1"; // 此处调用resolveInstanceMethod,动态添加并执行autoDictionarySetter NSLog(@"%@", obj.dynamicStr); // 此处调用resolveInstanceMethod,动态添加并执行autoDictionaryGetter obj.dynamicStr = @"2"; // 此处之调用autoDictionarySetter NSLog(@"%@", obj.dynamicStr); // 此处之调用autoDictionaryGetter [obj performSelector:NSSelectorFromString(kRemoveAllObjectsMethod)]; // 此处调用resolveInstanceMethod,没有动态添加;然后调用forwardingTargetForSelector返回备援接收者,备援接受者处理消息 NSLog(@"%@", obj.dynamicStr); obj.dynamicStr = @"3"; NSLog(@"%@", obj.dynamicStr); [obj performSelector:NSSelectorFromString(kClearAllObjectsMethod)]; // 此处调用resolveInstanceMethod,没有动态添加;然后调用forwardingTargetForSelector,返回nil;执行methodSignatureForSelector返回签名,然后执行forwardInvocation NSLog(@"%@", obj.dynamicStr);
0 0
- IOS消息转发机制
- oc消息转发机制
- Runtime_消息转发机制
- iOS消息转发机制
- OC-消息转发机制
- ios消息转发机制
- oc消息转发机制
- IOS 消息转发机制
- iOS消息转发机制
- 消息转发机制
- iOS消息转发机制
- OC消息转发机制
- 消息转发机制
- oc消息转发机制
- Runtime消息转发机制
- OC消息转发机制
- iosiOS消息转发机制
- Runtime消息转发机制
- 三点顺序
- Unity特效(1) 梦幻旋屏
- Git入门
- SSM框架整合
- Java_java中FileWriter和FileReader类(基本使用方法)
- 消息转发机制
- ZCMU--1457-旅行
- leetcode钻研5 链表的插入排序
- The file “xxx” couldn’t be opened because you don’t have permission to view it
- Spring 动态数据源和AOP实现数据库读写分离
- 人生就像盖楼 - 再见2016 你好2017
- 面向对象与编程思想
- shadowsocks+vps实现google搜索
- 【解决方案】Ext.grid.CheckboxSelectionModel 实现锁住指定行不被选中