第12条:理解消息转发机制(3)

23 篇文章 0 订阅

《Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法》第2章对象、消息、运行期,本章讲解运行期环境中各个部分协同工作的原理之后,你的开发水平将会进一步提升。本节为大家介绍消息转发机制。

第12条:理解消息转发机制(3)

本例的关键在于resolveInstanceMethod:方法的实现代码:
 

 
 
  1. + (BOOL)resolveInstanceMethod:(SEL)selector {  
  2.     NSString *selectorString = NSStringFromSelector(selector);  
  3.     if ([selectorString hasPrefix:@"set"]) {  
  4.         class_addMethod(self,  
  5.                         selector,  
  6.                         (IMP)autoDictionarySetter,  
  7.                         "v@:@");  
  8.     } else {  
  9.         class_addMethod(self,  
  10.                         selector,  
  11.                         (IMP)autoDictionaryGetter,  
  12.                         "@@:");  
  13.     }  
  14.     return YES;  
  15. }  
  16. @end 

当开发者首次在EOCAutoDictionary实例上访问某个属性时,运行期系统还找不到对应的选择子,因为所需的选择子既没有直接实现,也没有合成出来。现在假设要写入opaqueObject属性,那么系统就会以“setOpaqueObject:”为选择子来调用上面这个方法。同理,在读取该属性时,系统也会调用上述方法,只不过传入的选择子是opaqueObject。resolveInstanceMethod方法会判断选择子的前缀是否为set,以此分辨其是set选择子还是get选择子。在这两种情况下,都要向类中新增一个处理该选择子所用的方法,这两个方法分别以autoDictionarySetter及autoDictionaryGetter函数指针的形式出现。此时就用到了class_addMethod方法,它可以向类中动态地添加方法,用以处理给定的选择子。第三个参数为函数指针,指向待添加的方法。而最后一个参数则表示待添加方法的“类型编码”(type encoding)。在本例中,编码开头的字符表示方法的返回值类型,后续字符则表示其所接受的各个参数。

getter函数可以用下列代码实现:
 

 
 
  1. id autoDictionaryGetter(id self, SEL _cmd) {  
  2.         // Get the backing store from the object  
  3.         EOCAutoDictionary *typedSelf = (EOCAutoDictionary*)self;  
  4.         NSMutableDictionary *backingStore = typedSelf.backingStore;  
  5.  
  6.         // The key is simply the selector name  
  7.         NSString *key = NSStringFromSelector(_cmd);  
  8.  
  9.         // Return the value  
  10.         return [backingStore objectForKey:key];  

而setter函数则可以这么写:
 

 
 
  1. void autoDictionarySetter(id self, SEL _cmd, id value) {  
  2.         // Get the backing store from the object  
  3.         EOCAutoDictionary *typedSelf = (EOCAutoDictionary*)self;  
  4.         NSMutableDictionary *backingStore = typedSelf.backingStore;  
  5.  
  6.         /** The selector will be for example, "setOpaqueObject:".  
  7.           *  We need to remove the "set", ":" and lowercase the first  
  8.           *  letter of the remainder.  
  9.           */  
  10.         NSString *selectorString = NSStringFromSelector(_cmd);  
  11.         NSMutableString *key = [selectorString mutableCopy];  
  12.  
  13.         // Remove the ':' at the end  
  14.     [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];  
  15.  
  16.         // Remove the 'set' prefix  
  17.         [key deleteCharactersInRange:NSMakeRange(0, 3)];  
  18.  
  19.         // Lowercase the first character  
  20.         NSString *lowercaseFirstChar =  
  21.         [[key substringToIndex:1] lowercaseString];  
  22.     [key replaceCharactersInRange:NSMakeRange(0, 1)  
  23.                                                       withString:lowercaseFirstChar];  
  24.  
  25.         if (value) {  
  26.        [backingStore setObject:value forKey:key];  
  27.     } else {  
  28.         [backingStore removeObjectForKey:key];  
  29.     }  

EOCAutoDictionary的用法很简单:
 

 
 
  1. EOCAutoDictionary *dict = [EOCAutoDictionarynew];  
  2. dict.date = [NSDatedateWithTimeIntervalSince1970:475372800];  
  3. NSLog(@"dict.date = %@", dict.date);  
  4. // Output: dict.date = 1985-01-24 00:00:00 +0000 

其他属性的访问方式与date类似,要想添加新属性,只需用@property来定义,并将其声明为@dynamic即可。在iOS的CoreAnimation框架中,CALayer类就用了与本例相似的实现方式,这使得CALayer成为“兼容于键值编码的”(key-value-coding-compliant)容器类,也就等于说,能够向里面随意添加属性,然后以键值对的形式来访问。于是,开发者就可以向其中新增自定义的属性了,这些属性值的存储工作由基类直接负责,我们只需在CALayer的子类中定义新属性即可。

要点

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

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

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

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值