本篇为 Runtime 系列文章的第一篇, 其他文章的传送门见下方:
Runtime系列 2 -- 成员变量与属性:传送门
Runtime系列 3 -- 方法与消息:传送门
Runtime系列 4 -- Method Swizzling:传送门
Runtime系列 5 -- 协议与分类:传送门
接触过 OC 的小伙伴应该都知道一个黑科技 -- Runtime, 它让 OC 这门语言将很多静态语言在编译和链接时期做的事放到了运行时来处理. 可以让我们在运行时把消息转发给我们想要的对象, 或者随意交换一个方法的实现等. Runtime 其实就是一个 Runtime 库, 它基本上是用 C 和汇编写的, 将结构体封装成了对象, 使其有了面向对象的能力.
Runtime 库主要做了两件事:
① 封装: 正如上面介绍的一样, OC 中的对象是由 C 语言中的结构体封装而来, 而 OC 中的方法也是由 C 里的函数来实现的.
② 找到方法最终执行的代码: 当对象调用方法时, 最终都会由 runtime 来判断这个消息接收者时候可以响应消息而做出不同的反应.
我们现在使用的 OC 版本是 OBJC2, 但是在 xcode 里面看到的 runtime 文件里都是 #if !__OBJC2__ 这种判断, 想要真正了解 OBJC2 的小伙伴请点击 传送门 来查看 runtime.h
类与对象基础数据结构:
每个分享 runtime 知识的人必放的一张图, 先讲一下最基础的, 之后会慢慢深入
① isa: isa 的指向有两种, 如果是对象, isa 指向它的类, 如果是类, isa 指向它的 meta-class (元类).
② super_class: 如果这个类是 NSObject 或者 NSProxy, 则为 NULL, 如果不是的话, 就指向其父类.
③ version: 提供版本的信息, 对于对象的序列化非常有用(大神说的, 我其实也不懂). 默认是 0, 可以通过 runtime函数 class_setVersion 或者 class_getVersion 进行设置.
④ info: 类信息, 供运行时期使用的一些位标识,如 CLS_CLASS (0x1L) 表示该类为 class, CLS_META (0x2L) 表示该类为 meta-class.
⑤ instance_size: 该类的实例变量大小, 包括从父类继承下来的实例变量.
⑥ ivars: 该类的成员变量链表, 说白了就是成员变量地址列表.
⑦ methodLists: 该类方法定义的链表, 存放的值与 info 的一些标志位有关, CLS_CLASS (0x1L) 存储实例方法,CLS_META (0x2L) 则存储类方法
⑧ cache: 缓存列表, 用于缓存使用过的方法, 当接收者接收到消息时, 根据 isa 指针查找可以响应该消息的对象, 先从 cache 中查找, 如果没找到再去 methodLists 中查找方法.(我们每次调用的方法都会放至 cache 列表中). objc_cache 结构体这里就不说了.
⑨ protocols: 存储该类声明遵守的协议的列表.
Class 是一个指向 objc_class 的结构体指针, 同样 id 也是指向 objc_object 的结构体指针, id 就是通过 isa 指针去找到对应的类的.
Meta-Class(元类):
还记得很早之前我面试过一家公司, iOS 负责人就问过我一个面试题: "类方法和实例方法的相同点和不同点?" 当时我还是太傻太年轻回答 "+", "-" 这些区别(下面会说这道面试题的答案的, 先看看 meta-class)
对象的 isa 指针指向它的类, 其实类也是一个对象, 类的 isa 指针指向的就是 meta-class, 里面存放着这个类的类方法, meta-class 其实也是一个类, 它自己也有 isa 指针, 它的 isa 指针指向基类的 meta-class, 而基类的 meta-class 的 isa 指针指向的则是自己(而且 meta-class 继承自基类), 再放上去一张大家都见过的图吧:
下面就来说一下那道面试题的答案吧: 其实上面也已经说过了, 类方法和实例方法的相同点就是都是对象调用方法, 不同点就是方法存放的位置不同, 类方法存放在 meta-class 中, 实例方法存放在 class 中.
动态创建类和对象
runtime 可以在运行时创建类和对象, 那些又臭又长的方法名我就不往上面 copy 了, google 一搜一大把. 但是有一点要说明: oc 不支持往已存在的类中添加实例变量, 因此不管是系统库提供的提供的类, 还是我们自定义的类, 都无法动态添加成员变量。只可以在运行时创建的类中使用 class_addIvar 函数添加成员变量, 还只能在 objc_allocateClassPair 和 objc_registerClassPair 两个函数之间调用, 而且这个类也不能使 meta-class.
网友评论