您的位置:首页 > 移动开发 > IOS开发

iOS runtime学习之消息转发机制

2016-03-23 10:44 525 查看
在对象上调用方法,是OC中经常使用的功能。用OC的术语来说,这叫做“消息传递”。

在很多语言中,比如C,调用函数就是跳转到内存中某一点开始执行代码,没有任何动态性可言,因为在编译期就决定了。而OC不同,是在运行时发送消息的。这个消息,也许会由对象自己处理,也可能被转发给另一个对象,或者不予理睬。下面就说一下消息传递是如何工作的:

(1)检查接受对象是否为nil,如果是,调用nil处理程序

(2)检查类缓存中是不是已经有了这个方法,有了,就直接调用。

(3)比较请求的选择子和类中定义的选择子,找到了,调用其实现

(4)比较请求的选择子和父类中的选择子,再到父类的父类,以此类推,找到了,调用其实现

(5)以上四步还找不到,就要用到消息转发机制了

(6)调用resolveInstanceMethod:(或resolveClassMethod:)方法。如果返回YES,那么重新开始,这个时候对象会响应这个选择子,一般因为它已经调用了class_addMethod方法。

(7)(如果6的返回为NO)调用快速转发方法给备援接受者,forwardingTargetForSelector:,如果返回非nil,就把消息发送到返回的对象上。这里不要返回self,否则会形成死循环。

(8)调用methodSignatureForSelector:,如果返回非nil,创建一个NSInvocation对象,把与尚未处理的那条消息有关的全部细节封装于其中。此对象包括选择子、目标(target)及参数。将此对象传给forwardInvocation:方法。

(9)调用doesNotRecognizeSelector:,默认的实现是抛出异常。

消息转发流程:



例子一:(7)验证

@implementation Test1

- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"eat"]) {
return [[Test2 alloc] init];
}
return nil;
}
@end

@implementation Test2

- (void)eat {
NSLog(@"test2 : eat");
}
@end

int main(int argc, const char * argv[]) {
@autoreleasepool {
Test1 *test1 = [[Test1 alloc] init];
objc_msgSend(test1, @selector(eat), @"DiSanXian");
}
return 0;
}


例子二:(8)验证

(8)方法可以简单的实现,只需要改变调用目标,将消息在调用目标上实现即可。然而这样做和(7)就“等效”了,消息传递到这其实开销还是挺大的,越往下开销越大,所以很少有人这么实现。那么问题就来了,该怎么实现呢?比较有用的实现方式是:在触发消息之前,先以某种方式改变消息内容,比如,追加另外一个参数,或者改变选择子,等等。

@implementation Test1

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"eat"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
anInvocation.selector = @selector(eatWithFood:);
[anInvocation invokeWithTarget:[[Test2 alloc] init]];
}
@end

@implementation Test2

- (void)eatWithFood:(NSString *)name {
NSLog(@"foodName: %@", name);
}

@end

int main(int argc, const char * argv[]) {
@autoreleasepool {
Test1 *test1 = [[Test1 alloc] init];
objc_msgSend(test1, @selector(eat), @"DiSanXian");

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: