从方法执行过程看rumtine/message机制

简介

Objective-C是开发苹果软件的语言, 大部分是C语言.除去一些基本的特性, 最重要的它是一门动态语言, 其动态性的基石便是rumtime, 即运行时机制.可以说OC没有运行时就没有了魅力.

消息机制

对于静态语言, 执行方法显得比较死,方法名就能决定了此次方法执行的全部因素, 但是对于消息分发机制来说,就很灵活。编译阶段一切都是未定的, 并不知道对象是否能处理, 也不知道最终是哪个对象处理, 也不知道最终调用的方法实现是什么, 而这一切都是在运行时决定.

下面我们来详细剖析一下方法调用的整个的过程

- (void)viewDidLoad {
    [super viewDidLoad];

    Dog *dog = [[Dog alloc] init];
    [dog doSomething];
}

[dog doSomething]

  1. [dog doSomething]干了什么?
    编译器将
    [dog doSomething] --->
    objc_msgSend(dog, @selector(doSomething))

  2. objc_msgSend(dog, @selector(doSomething)) 干了什么?这是重点, 详细讲解:

    1. 检查dog是个什么类型的对象,即查找dog的Class, OC对象本身有一个isa成员, 这个isa决定了对象的类型, 即isa指针所指向的Class是dog的真实类型, 而不在乎用什么类型的指针接收这个对象, 这是多态的基础.
    • 这一步可以玩的花样:
      如果在 [dog doSomething]这句代码执行之前(注意一定要之前), 改变dog的isa指针的指向, 改为 Cat 类型, 那么处理这条消息的对象就不是Dog类型了, 而是Cat类型. OC中KVO就是用这种方式来实现的, 可以看我这篇文章KVO的原理, 底层实现.
    1. OC中class维护一份方法列表objc_method_list,
      这个列表中存放着SEL, 即相当于方法名, 也可以说是方法的id. 这一步系统会查找class中objc_method_list中有没有doSomething, 如果没有则向其superclass的objc_method_list中去查找.
    • 这一步可以玩的花样:
      同理在这一步之前, 即使没有显式的doSomething方法声明和实现, 只要利用class_addMethod来动态添加doSomething方法即可. 但是需要用到performSelector相关的方法来调用以保证编译通过.
    1. 找到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强大的地方.

觉得有用的的猿友点个赞!

你可能感兴趣的:(从方法执行过程看rumtine/message机制)