简介
Objective-C是开发苹果软件的语言, 大部分是C语言.除去一些基本的特性, 最重要的它是一门动态语言, 其动态性的基石便是rumtime, 即运行时机制.可以说OC没有运行时就没有了魅力.
消息机制
对于静态语言, 执行方法显得比较死,方法名就能决定了此次方法执行的全部因素, 但是对于消息分发机制来说,就很灵活。编译阶段一切都是未定的, 并不知道对象是否能处理, 也不知道最终是哪个对象处理, 也不知道最终调用的方法实现是什么, 而这一切都是在运行时决定.
下面我们来详细剖析一下方法调用的整个的过程
- (void)viewDidLoad {
[super viewDidLoad];
Dog *dog = [[Dog alloc] init];
[dog doSomething];
}
[dog doSomething]
[dog doSomething]
干了什么?
编译器将
[dog doSomething]
--->
objc_msgSend(dog, @selector(doSomething))
-
objc_msgSend(dog, @selector(doSomething))
干了什么?这是重点, 详细讲解:- 检查dog是个什么类型的对象,即查找dog的Class, OC对象本身有一个isa成员, 这个isa决定了对象的类型, 即isa指针所指向的Class是dog的真实类型, 而不在乎用什么类型的指针接收这个对象, 这是多态的基础.
- 这一步可以玩的花样:
如果在[dog doSomething]
这句代码执行之前(注意一定要之前), 改变dog的isa指针的指向, 改为Cat
类型, 那么处理这条消息的对象就不是Dog类型了, 而是Cat类型. OC中KVO就是用这种方式来实现的, 可以看我这篇文章KVO的原理, 底层实现.
- OC中class维护一份方法列表
objc_method_list
,
这个列表中存放着SEL, 即相当于方法名, 也可以说是方法的id. 这一步系统会查找class中objc_method_list
中有没有doSomething
, 如果没有则向其superclass的objc_method_list
中去查找.
- 这一步可以玩的花样:
同理在这一步之前, 即使没有显式的doSomething
方法声明和实现, 只要利用class_addMethod
来动态添加doSomething
方法即可. 但是需要用到performSelector
相关的方法来调用以保证编译通过.
- 找到
doSomething
这个SEL之后, 系统就会调用, 注意, 这个SEL仍然不能决定系统最终执行的代码是什么, 决定者是与SEL有映射关系的IMP, 简单说每一个SEL只相当于一个字典的key, 这个key所对应的value才真正决定了方法的实现, 这一步只是系统通过SEL去找到这个value, 这个value就是IMP, IMP是一个函数指针, 指向方法的实现, IMP决定了最终调用哪段代码.
- 这一步可以玩的花样:
在函数执行之前, 如果改变了doSomething
这个SEL对应的IMP, 改为另一个方法eat
的IMP, 那么这句[dog doSomething]
最终的结果是dog
执行了eat
的实现.method_exchangeImplementations
这个函数便是通过这种方式来做到方法交换的.
从上面可以看到一句编译阶段的[dog doSomething]
基本决定不了任何东西, 在以上运行过程中随意改变一些东西,就可以使[dog doSomething]
"面目全非". 这也是rumtime强大的地方.
觉得有用的的猿友点个赞!