Objective-C runtime之消息转发机制(三)

来源:互联网 发布:nginx多ip域名绑定 编辑:程序博客网 时间:2024/04/29 07:48
学了那么久的Objective-C,给我的感觉就是它什么都是动态的,你将会听到一个新的名词:

一、动态方法解析

1、+(BOOL) resolveInstanceMethod:(SEL) sel

这是NSObject根类提供的类方法,调用时机为当被调用的方法实现部分没有找到,而消息转发机制启动之前的这个中间时刻。

2、@dynamic关键字

Objective-C2.0 提供了@dynamic关键字,从名字上可以看出来,它一定是跟动态相关的。猜对了,这个关键字告诉编译器,某个属性的实现是动态的。如果我们在@interface接口文件中声明了一个属性,如下所示:

[cpp] view plaincopy
  1. @property(nonatomic,retain) NSString    *name;  
在@implementation文件中做了如下声明:

[cpp] view plaincopy
  1. @dynamic name;  
就表示编译器须动态地生成该属性对应的方法。如果想将该属性的-(void)setName:(NSString *)name方法用dynamicMethodIMP函数来代替,可以这样做:

[cpp] view plaincopy
  1. void dynamicMethodIMP(id self, SEL _cmd)  
  2. {  
  3.     // implementation ....  
  4. }  
  5.   
  6. + (BOOL)resolveInstanceMethod:(SEL)sel  
  7. {  
  8.     NSLog(@"sel is %@", NSStringFromSelector(sel));  
  9.     if(sel == @selector(setName:)){  
  10.         class_addMethod([self class],sel,(IMP)dynamicMethodIMP,"v@:");  
  11.         return YES;  
  12.     }  
  13.     return [super resolveInstanceMethod:sel];  
  14. }  

在resolveInstanceMethod的实现中,我们通过class_addMethod方法动态的向当前对象增加了dynamicMethodIMP函数,来代替-(void)setName:(NSString *)name的实现部分,从而达到了动态生成name属性方法的目的。

值得说明的是:

①在我看来,@dynamic关键字的作用并非想象中的那么大,理由是,如果没有在@implementation文件中做出@dynamic声明,而在resolveInstanceMethod方法中做了方法重定向的工作,一样能顺利的调用dynamicMethodIMP函数。唯一的不同之处在于进行了@dynamic声明时,编译器不会告警而已!!

②在上个例子中,我们自己实现了-(void)setName:(NSString *)name方法,则在运行的时候,调用完我们实现的-(void)setName:(NSString *)name方法后,运行时系统仍然会调+(BOOL) resolveInstanceMethod:(SEL) sel方法,只不过这里的sel会变成_doZombieMe,从而我们实现重定向的if分支就进不去了。

③"v@:"属于Objective-C类型编码的内容,感兴趣的同学可以自己google一下。

二、runtime system消息转发机制

对象是谦恭的,它会接收所有发送过来的消息,哪怕这些消息自己无法响应。问题来了:当对象无法响应这些消息时怎么办?runtime提供了消息转发机制来处理该问题。

当外部调用的某个方法对象没有实现,而且resolveInstanceMethod方法中也没有做重定向处理时,就会触发- (void)forwardInvocation:(NSInvocation *)anInvocation方法。在该方法中,可以实现对不能处理的消息做的一些默认处理,也可以以其它的某种方式来避免错误被抛出。像forwardInvocation:的名字一样,这个方法通常用来将不能处理的消息转发给其它的对象。通常我们重写该方法的方式如下所示:

[cpp] view plaincopy
  1. -(void)forwardInvocation:(NSInvocation *)invocation  
  2. {  
  3.     SEL invSEL = invocation.selector;  
  4.     if ([someOtherObject respondsToSelector:invSEL])  
  5.         [anInvocation invokeWithTarget:someOtherObject];  
  6.     } else {  
  7.         [self doesNotRecognizeSelector:invSEL];   
  8.     }                                                                            
  9. }  

怎么看着有点像多继承呀???你说对了,消息转发提供了多重继承的很多特性。然而,两者有很大的不同:多重继承是将不同的行为封装到单个的对象中,有可能导致庞大的,复杂的对象。而消息转发是将问题分解到更小的对象中,但是又以一种对消息发送对象来说完全透明的方式将这些对象联系起来。总之,Objective-C通过这种方式,一定程度上减小了自己不支持多继承的劣势。


经过半个月的时间,自己总结、整理出了这三篇文章,到这里,对Objective-C运行时的学习算是告一段落了。文笔的原因,文章结构不是很清晰,还请见谅。对运行时理解不到位,或者是有错误的地方,还请广大博友指出,感激不尽!

原创粉丝点击