Effective Objective-C 2.0 编写高质量iOS与OS X代码 理解消息转发机制

1. 若对象无法相应某个选择子,则进入消息转发流程。

2. 通过运行期的动态方法解析功能,我们可以在需要用到某个方法时再将其加入类中。

3. 对象可以把其无法解读的某些选择子转交给其他对象来处理。

4. 经上述两步,如果还是没办法处理选择子,就启动完整的消息转发机制。



5. 消息转发分为两个大部分。
第一阶段征询接收者所属的类,看其能否动态添加方法,以处理当前那个“位置的选择子”(unknown selector),这叫做“动态方法解析”(dynamic method resolution)。
第二阶段,若有备援的接收者,则传递给那个对象,否则启动完整的消息转发机制。
运行期系统会把与消息有关的全部细节都封装到NSInvocation对象里,再给接收者最后一次机会,令其设法解决当前还未处理的这条消息。

6. 动态方法解析。
对象在接受到无法解读的消息后,首先将调用其所属类的下列类方法:
+ (BOOL)resolveInstanceMethod:(SEL)selector

id autoDictionaryGetter(id self, SEL _cmd);
void autoDictionarySetter(id self, SEL _cmd, id value);

+ (BOOL)resolveInstanceMethod:(SEL)selector
{
     NSString *selectorString = NSStringFromSelector(selector);
     if (/* selector is from a @dynamic property */)
     {
          if([selectorString hasPrefix:@“set”])
          {
               class_addMethod(self, selector, (IMP)autoDictionarySetter, “v@:@“);
          }else {
               class_addMethod(self, selector, (IMP)autoDictionaryGetter, “@@:“);
          }
          return YES;
     }
     return [super resolveInstanceMethod:selector];
}


7. 备援接收者
- (id)forwardingTargetForSelector:(SEL)selector
注:我们无法操作经由这一步所转发的消息,若是想再发送给备援接收者之前修改消息内容,那就要通过完整的消息转发机制来做了。

8. 完整的消息转发。
首先创建NSInvocation对象,将尚未处理的那条消息有关的全部细节都封装其中。
此对象包括选择子、目标(targer)及参数。
再触发NSInvocation对象时,“消息派发系统”将亲自出马,把消息指派给目标对象。
- (void)forwardInvocation:(NSInvocation*)invocation;
实现此方法时,若发现某调用操作不应由本类处理,则需调用超类的同名方法。
这样继承体系中的每个类都有机会处理此调用请求,直至NSObject。
如果最后调用了NSObject类的方法,那么该方法还会继而调用“doesNotRecognizeSelector”,以抛出异常。
此异常表明选择子最终未能得到处理。

9. 以完整的例子演示动态方法解析
设计思路:由开发者来添加属性定义,并将其声明为@dynamic,而类则会自动处理相关属性值的存放与获取操作。
@interface EOCAutoDictionary : NSObject
@property (nonatomic, strong) NSString *string;
@property (nonatomic, strong) NSNumber *number;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) id opaqueObject;
@property (nonatomic, strong) NSMutableDictionary *backingStore;
@end 

@interface EOCAutoDictionary
@dynamic string, number, date, opaqueObject;

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

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



当开发者首次在EOCAutoDictionary实例上访问某个属性时,运行期系统还找不到对应的选择子,因为所需的选择子既没有直接实现,也没有合成出来。所以会调用resolveInstanceMethod:继而调用class_addMethod方法。

getter 函数可以用下列代码实现:
id autoDictionaryGetter (id self, SEL _cmd)
{
     // Get the backing store from the object 
     EOCAutoDictionary *typedSelf = (EOCAutoDictionary*)self;
     NSMutableDictionary *backingStore = typedSelf.backingStore;
     
     // The key is simply the selector name
     NSString *key = NSStringFromSelector(_cmd);
     
     // Return the value
     return [backingStore objectForKey:key];
}



而 setter 函数可以这么写:
void autoDictionarySetter(id self,SEL _cmd, id value)
{
     // Get the backing store from the object;
     EOCAutoDictionary *typedSelf = (EOCAutoDictionary*)self;
     NSMutableDictionary *backingStore = typedSelf.backingStore;
 
     // for “ :setOpaqueObject:” tobe “opaqueObject”
     NSString *selectorString = NSStringFromSelector(_cmd);
     NSMutableString *key = [selectorString mutableCopy];

     [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];

     [key deleteCharactersInRange:NSMakeRange(0, 3)];
     
     NSString 8lowercaseFistChar = [[key substringToIndex:1] lowercaseString];
     [key replaceCharactersInRange:NSMakeRange(0,1) withString:lowercaseFirstChar];

     if(value)
          [backingStore setObject:value forKey:key];
     else
          [backingStore removeObjectForKey:key];
}



iOS 的 CALayer 类似本例的实现方式。使得CALayer成为“兼容于键值编码的”容器类(key-value-coding-compliant);

摘取自:《 Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法 》,详细请购买书籍,支持作者及译者。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值