嗯,运行时,运行时是个好东西。在Objective-C语言中,这个特性可以帮助我们干很多的事情。
首先这个特性是把代码的决策从编译和链接时变成运行的时候,这样我们就可以用这个特性来做一些只有在运行的时候才能做到的东西,具体包括:
1.swizzling (交换两个方法的实现)
2.动态方法(可以在运行的时候对一个类进行方法的增加,我们这篇博客会主要讲解这个)
3.相关引用(可以把一个类和一个对象使用一个key进行关联)
4.内省(判断一个对象是否可以调用一个方法)
5.使用emoji字符作为方法名
我们分为两部分讲解消息转发,一种是在本类中增加一个对于无法响应的方法的实现,一个是让另一个对象来响应用户调用的方法。
我们首先知道,当我们调用一个对象的一个方法的时候,会通过*isa真寻找本类中对于该方法的实现IMP,如果本类找不到,则会向父类寻找,当走到NSObject都无法调用时,程序会crash掉。但是在crash之前,运行时为我们提供了一个机制就是可以动态地增加这个方法的实现或者让另一个对象来响应这个方法。
我们先看看动态增加一个方法吧,比如我们调用了对象无法响应的doFoo方法,但是我们在类中有一个方法实现的声明:
void fooMethod(id obj, SEL _cmd) {
NSLog(@"hehe");
}
我们希望用这个方法来实现doFoo方法,那么我们就需要重写
+(
BOOL
)resolveInstanceMethod:(
SEL
)aSEL方法,具体如下:
+(BOOL)resolveInstanceMethod:(SEL)aSEL
{
if(aSEL == @selector(doFoo)){
class_addMethod([self class],aSEL,(IMP)fooMethod,"v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
打一下断点,我们就可以看到控制台输出了"hehe".
以上就是动态增加这个方法的实现的过程。
第二个就是让另一个对象响应这个方法。
系统会先走入- (id)forwardingTargetForSelector:(SEL)aSelector方法来进行转发,我们就需要重写这个方法,具体如下:
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if(aSelector == @selector(doFoo)){
return self.alternativeObject;
}
return [super forwardingTargetForSelector:aSelector];
}
系统获得了这个对象之后,就会使用-(void)forwardInvocation:(NSInvocation *)invocation方法来把方法统一进行执行,具体如下:
-(void)forwardInvocation:(NSInvocation *)invocation
{
SEL invSEL = invocation.selector;
if([self.alternativeObject respondsToSelector:invSEL]) {
[invocation invokeWithTarget:self.alternativeObject];
} else {
[self doesNotRecognizeSelector:invSEL];
}
}
好了,我们在alternativeObject中响应方法打断点,我们发现执行到了!~
以上就是要说明的啦~~