RunTime运行时(三)
2017-06-06 本文已影响0人
飘金
转自原文地址:http://blog.csdn.net/wzzvictory/article/details/8629036
学了那么久的Objective-C,给我的感觉就是它什么都是动态的,你将会听到一个新的名词:
一、动态方法解析
-
1、+(BOOL) resolveInstanceMethod:(SEL) sel
这是NSObject根类提供的类方法,调用时机为当被调用的方法实现部分没有找到,而消息转发机制启动之前的这个中间时刻。
-
2、@dynamic关键字
Objective-C2.0 提供了@dynamic关键字。这个关键字有两个作用:
①告诉编译器不要创建实现属性所用的实例变量;
②告诉编译器不要创建该属性的get和setter方法。
如果我们在@interface接口文件中声明了一个属性,如下所示:
@property(nonatomic,retain) NSString *name;
默认情况下,编译器会为当前类自动生成一个NSString *_name的实例变量(如果想改变实例变量的名称可以用@synthesize关键字),同时会生成两个名为- (NSString *)name和- (void)setName:(NSString *)aName的存取方法。
而@dynamic关键字就是告诉编译器不要做这些事,同时在使用了存取方法时也不要报错,即让编译器相信存取方法会在运行时找到。
比如在@implementation文件中做了如下声明:
@dynamic name;
如果使用了name属性的setter方法,又不想在运行时崩溃,就可以在运行时做点动作:
void dynamicMethodIMP(id self, SEL _cmd)
{
// implementation ....
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"sel is %@", NSStringFromSelector(sel));
if(sel == @selector(setName:)){
class_addMethod([self class],sel,(IMP)dynamicMethodIMP,"v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
在resolveInstanceMethod的实现中,我们通过class_addMethod方法动态的向当前对象增加了dynamicMethodIMP函数,来代替-(void)setName:(NSString *)name的实现部分,从而达到了动态生成name属性方法的目的。
值得说明的是:
①在上个例子中,我们自己实现了-(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:的名字一样,这个方法通常用来将不能处理的消息转发给其它的对象。通常我们重写该方法的方式如下所示:
-(void)forwardInvocation:(NSInvocation *)invocation
{
SEL invSEL = invocation.selector;
if ([someOtherObject respondsToSelector:invSEL])
[anInvocation invokeWithTarget:someOtherObject];
} else {
[self doesNotRecognizeSelector:invSEL];
}
}