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)验证
例子二:(8)验证
(8)方法可以简单的实现,只需要改变调用目标,将消息在调用目标上实现即可。然而这样做和(7)就“等效”了,消息传递到这其实开销还是挺大的,越往下开销越大,所以很少有人这么实现。那么问题就来了,该怎么实现呢?比较有用的实现方式是:在触发消息之前,先以某种方式改变消息内容,比如,追加另外一个参数,或者改变选择子,等等。
在很多语言中,比如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; }
相关文章推荐
- ios试图
- iOS中数组转字符串,字符串转数组
- IOS属性(@property)的修饰词assign、weak、strong、retain、copy、nonatomic、atomic、readonly、readwrite解释、以及一部分内存优化方法
- iOS 强弱引用
- 蓝欧iOS进阶考试绝密
- iOS开发~CocoaPods使用详细说明
- iOS 关于navigationBar的一些:毛玻璃、透明、动态缩放、动态隐藏
- GitHub上7个非常值得学习的iOS开源项目
- iOS多线程篇之GCD
- iOS-多级presentViewController直接返回一级界面
- iOS 蓝牙4.0开发
- ios的位置和方向(来自苹果官方文档,仅供简单参考)
- ios的位置和方向(来自苹果官方文档,仅供简单参考)
- 详解iOS应用开发中autoresizing尺寸自动适应属性的用法
- m4a文件在iOS上的流媒体播放
- ios启动过程
- iOS 中的数据持久化2.0-bruceyou1990
- iOS动态分享收藏举报弹出框
- iOS Safari阅读模式分析过程
- iOS Safari阅读模式研究