RunTime程序员

Objective-C runtime 的简单理解与使用(一)

2016-03-17  本文已影响351人  yahtzee_

简书上的所有内容都可以在我的个人博客上找到(打个广告😅)


一开始接触 iOS 的时候,学的是 Swift。还有一个学期就大四,为了实习,也为了更好的理解 iOS,最近一个多月开始正式学习 Objective-C。而 runtime “运行时” 应该算是 Objective-C 的核心所在。我觉得有必要好好来理解一下 runtime。学的还不是很精,主要是通过看别人的博客和《Effective Objective-C 2.0》这本书来学习 runtime ,官方文档还没有看过,希望以后能够补上。如果有什么理解的不对的地方,希望大家指出。😆

Objective-C中的消息分发


[someObject doSomething];

这是我们在 Objective-C 中调用方法的方式, 但其实这应该称作“传递消息”(pass a message),给 someObject 对象传递一个 doSomething的消息。因为这行代码在编译时会变成这样:

objc_msgSend(someObject, @selector(doSomething))

这是个 C 函数, 声明在 objc/message.h 头文件中,它接受两个及以上的可变参数, 第一个参数代表的是消息的接收者,第二个参数代表选择子(selector,在后面我们会细说),如果要调用的方法还有别的参数,就会跟在这两个参数后面。还有其他几个类似的函数,来处理不同的情况:

id objc_msgSend(id self, SEL op, ...)   // 直接发送消息
id objc_msgSendSuper(struct objc_super *super, SEL op, ...) // 给父类发送消息
void objc_msgSend_stret(id self, SEL op, ...)   // 返回值是结构体时,可以交给这个函数处理
double objc_msgSend_fpret(id self, SEL op, ...) // 返回值是浮点数时,可以交给这个函数处理
......

当我们向一个对象发送一个消息时,它会在这个对象的类中根据 selector 找到真正的实现函数,如果找不到就再到它的父类中找,一层一层的找上去,最后到 NSObject 类中, 如果还没有找到,那么就会启用“消息转发机制”。这个我们以后再提。

消息分发图示

Objective-C中类的本质


Objective-C 中的类其实是 C 中的结构体,它的基本定义如下:

struct objc_class {
    Class isa ;
    Class super_class ;
    const char *name ;
    long version ;
    long info ;
    long instance_size ;
    struct objc_ivar_list *ivars ;
    struct objc_method_list **methodLists ;
    struct objc_cache *cache ;
    struct objc_protocol_list *protocols ;
} ;
/* Use `Class` instead of `struct objc_class *` */

我们来看一下其中比较重要的几个变量

Class isa

isa 指针,表示这个对象是一个什么类。而 Class 类型, 也就是 struct objc_class *** ,这是苹果在下面的注释中写到的。这说明类本身也是一个对象。在类对象中的 isa 指向的类叫做“元类”**,类方法就定义在元类中。
总的来说就是,一个类可以有很多的实例,这些实例有着唯一的一个类对象,而这个类对象也有着唯一的一个元类。

实例,类,父类,元类之间的关系

Class super_class

super_class 指向的就是它的父类。

struct objc_ivar_list *ivars

ivars 指向的是成员变量的列表。

struct objc_method_list **methodLists

methodLists 指向的就是方法的列表。在 method_list 中存着 objc_method 类型的数组。而 objc_method 的定义如下:

struct objc_method {
    SEL method_name ;
    char *method_types ;
    IMP method_imp ;
} 
typedef id (*IMP)(id, SEL, ...);    // IMP 就是一个函数指针

看到这个结构体, 我们应该就很明确 selector 的意义了。selector 其实就是方法的一个标示,而 method_imp 指向的才是真正的函数实现。当我们向对象发送消息后, runtime根据 selector 这个标示,在method_list中找到对应的 objc_method,取到真正的函数的地址,再执行。

struct objc_cache *cache

cache 用来缓存最近调用过的的方法。 如果每次向对象发送消息都要遍历一遍方法列表那会很浪费时间, 所以会把最近调用过的方法放在缓存中。每一次发送消息时,会先查询缓存,缓存中找不到再去方法列表中找。

struct objc_protocol_list *protocols

protocols 指向协议列表。

Runtime能做什么


上一篇下一篇

猜你喜欢

热点阅读