Objective-C Runtime基础知识

转自:https://zhuanlan.zhihu.com/p/27248527

1 个月前

Objective-C 扩展了 C 语言,并加入了面向对象特性,这个扩展的核心是一个用 C 写的 Runtime 库。它是 Objective-C 面向对象和动态机制的基石。每次 Apple 公司升级 Runtime 库, 所有使用 Objective-C开发的app 都收益,不得不佩服这里的设计哲学。我们可以自定义的类跟Objective-C Runtime 库交互,通过 Runtime 特性,实现一些特殊的需求。

objc/runtime.h

主要有 objc_method,objc_ivar, objc_category, objc_property, objc_class 数据结构。

Objective-C Runtime基础知识_第1张图片

我们需要了解 Runtime 的目标:

1. 了解这些数据结构;

2. 这些数据结构对应操作方法(API使用),源码链接:SwiftZimu/RuntimeLearning

3. 优秀的开源库Mantle库是怎么使用 runtime 特性的。参考另一篇文章《 Mantle 源码分析》;

4. Objective-C 消息转发原理。参考另一篇文章《Objective-C 消息转发机制详解》;

完成上面4个步骤,算是对 Objective-C runtime 有些了解了。

1. runtime 中的数据结构分析

在 objc/objc.h 上定义了:

SEL是 objc_selector 结构指针, 可以理解为区别方法的 ID,IMP是函数指针。

Objective-C Runtime基础知识_第2张图片

id是 objc_object 结构指针,是实例对象(instance object),在 objc_object 里有一个 isa 成员,是 Class 类型,表示它的类是谁。Class 是 objc_class 结构指针,类对象(class object), 定义在第一张图里的,它也有 isa 成员,表示它的元类是谁。

super_class: 指向该类的父类,如果是根类(root class, NSObject或者 NSProxy),那么 super_class 为 nil.

Objective-C Runtime基础知识_第3张图片

【图片来自网络】

所有的 metaClass isa 都指向 meta Root Class, 而 meta Root Class 的 isa 指向自己,并且从 Root Class (NSObject, NSProxy)继承过来。

1.1 objc_method

method_name 是 SEL类型的方法名 , objc_selector 结构指针, objc_selector 定义:What is the objc_selector implementation?

method_types 存储方法的参数类型和返回值类型。详细参考下图 Type Encoding。

method_imp 方法的具体实现, 函数指针。

1.2 objc_method_list

Objective-C Runtime基础知识_第4张图片

method_count 方法个数

Objective-C Runtime基础知识_第5张图片

space 占用空间

method_list 可变长度的 objc_method 结构数组

1.3 objc_ivar

ivar_name 变量名

Objective-C Runtime基础知识_第6张图片

ivar_type 变量类型 Type Encoding.(参考上面的图)

ivar_offset 基地址偏移字节

1.4 objc_ivar_list

ivar_count 变量个数

Objective-C Runtime基础知识_第7张图片

space 占用空间

ivar_list 可变长度的 objc_ivar 结构数组

1.5 objc_category

category_name 类别的名称

Objective-C Runtime基础知识_第8张图片

class_name 给哪个类增加类别

instance_methods 类别的实例方法

class_methods 类别的类方法

protocols 类别实现的协议列表

objc_protocol_list 是一个链表,count 表示多少个 可变长的Protocol数组, Protocol 也是 objc_object 结构。

1.6 objc_property_attribute_t

1.7 objc_cache

Objective-C Runtime基础知识_第9张图片

mask: 指定分配cache buckets的总数。在方法查找中,Runtime使用这个字段确定数组的索引位置。

occupied 实际占用cache buckets的总数。

buckets 指定Method数据结构指针的数组。这个数组可能包含不超过mask+1个元素。需要注意的是,指针可能是NULL,表示这个缓存bucket没有被占用,另外被占用的bucket可能是不连续的。这个数组可能会随着时间而增长;

objc_msgSend 每调用一次方法后,就会把该方法缓存到cache列表中,下次的时候,就直接优先从cache列表中寻找,如果cache没有,才从methodLists中查找方法。

2. Runtime 中的数据结构操作方法(API 使用)

本节主要内容:【内容比较多,按照成员变量、方法、属性、协议这种顺序来组织, 每小节后面都有一个例子 】

2.1 获取 super class,name, version, instance size

2.2 获取 ivarList, methodList, propertyList, protocolList

2.3 添加 ivar, method, property, protocol

2.4 添加 class

2.5 ivar 相关

2.6 method 相关

2.7 Selector 相关

2.8 IMP 相关

2.9 property 相关

2.10 protocol 相关

2.11 Associative References 相关

2.1 获取 super class,name , version, instance size

2.1.1 父类 super class 【 getSuperclass 】

2.1.2 类名 name 【 getName 】

Objective-C Runtime基础知识_第10张图片

2.1.3 类的版本信息 version 【 getVersion 】

Objective-C Runtime基础知识_第11张图片

2.1.4 类对象的大小 instance size 【 getInstanceSize 】

Objective-C Runtime基础知识_第12张图片

2.1.5 Example

Objective-C Runtime基础知识_第13张图片
Objective-C Runtime基础知识_第14张图片

2.2 获取 ivarList, methodList, propertyList, protocolList

2.2.1 成员变量列表 ivar list 【 copyIvarList 】

Objective-C Runtime基础知识_第15张图片

输出了 RLAccount 所有的成员变量,其中红色圈中的是私有变量,name,age 是属性,内部自动添加了_name, _age 成员变量。 ivar_getName 使用方法参考 2.5 Ivar 相关 内容。

Objective-C Runtime基础知识_第16张图片

2.2.2 方法列表 method list 【 copyMethodList 】

Objective-C Runtime基础知识_第17张图片
Objective-C Runtime基础知识_第18张图片

上图输出了 age, name 属性的 get, set 方法, 除此之外,Objective-C 对 RLAccount 类,自动增加了.cxx_destruct, 通过名字可以猜出是析构方法,用于释放资源。不包含父类 (NSObject)中的方法。

2.2.3 属性列表 property list 【 copyPropertyList 】

Objective-C Runtime基础知识_第19张图片
Objective-C Runtime基础知识_第20张图片

打印了自己的属性, name和 age,不打印父类的属性。property_getName 参考 2. 9 小节 property 相关。

2.2.4 采用的协议列表 protocol list 【 copyProtocolList 】

Objective-C Runtime基础知识_第21张图片

打印了自己实现的协议,不包括父类的协议。protocol_getName 参考 2.10 小节 protocol 相关

Objective-C Runtime基础知识_第22张图片

2.3 添加 ivar, method, property, protocol

2.3.1 添加成员变量 【 add Ivar 】

Objective-C Runtime基础知识_第23张图片

在现有的 RLAccount 类上,增加一个 _email 成员变量,结果增加失败,看接口说明是,不支持在现有的类上增加成员变量。只能在 objc_allocateClassPair 新建的类上添加,参考 2.4 小节 添加 class。

Objective-C Runtime基础知识_第24张图片

2.3.2 添加方法 【 add Method 】

Objective-C Runtime基础知识_第25张图片

添加了一个 newMethodOperation 方法,该方法的实现,由 IMP 指定,通过 一个 block指定。参考 2.8 小节 IMP 相关。

Objective-C Runtime基础知识_第26张图片

2.3.3 添加属性 【 add Property 】

Objective-C Runtime基础知识_第27张图片

添加了一个名字为 newPropertyName 的属性, 它的类型 为 NSString(T), readOnly(R) 的 。objc_property_attribute_t 更多方法参考 2.9 小节 property 相关。

Objective-C Runtime基础知识_第28张图片

2.3.4 添加协议 【 add Protocol 】

Objective-C Runtime基础知识_第29张图片
Objective-C Runtime基础知识_第30张图片

对 RLAccount 类实现一个 Swift4Indexable 协议。更多协议内容,请参考 2.10小节 protocol 相关。

2.4 添加 class

2.4.1 创建一个新类 alloc class pair

Objective-C Runtime基础知识_第31张图片

2.4.2 注册类 register class pair

Objective-C Runtime基础知识_第32张图片

2.4.3 释放创建的类 dispose class pair

2.4.4 Example:

Objective-C Runtime基础知识_第33张图片

知乎专栏添加了一个 UserInfo类,设置了一个 email成员变量。使用 KVO 设置值,值为本人的邮箱([email protected], 欢迎联系进行技术交流)。注册了 printLoginInfo 方法,可以同过设置的 imp 中的 block 进行回调,也可以发送消息的方式。最后释放实例对象和类对象。

Objective-C Runtime基础知识_第34张图片

sel_registerName 参考 2.7 小节 SEL 相关。消息转发的相关内容,参考另一篇文章《Objective-C 消息转发机制详解》

2.5 ivar 相关

2.5.1 ivar name 【 变量名 】

Objective-C Runtime基础知识_第35张图片

2.5.2 ivar type encoding 【 变量类型 】

Objective-C Runtime基础知识_第36张图片

2.6 method 相关

2.6.1 method name 【 方法名 】

Objective-C Runtime基础知识_第37张图片

2.6.2 method type encoding 【 方法参数类型及返回类型 】

Objective-C Runtime基础知识_第38张图片

2.6.3 获取 method implementation

Objective-C Runtime基础知识_第39张图片

2.6.4 设置 method implementation

Objective-C Runtime基础知识_第40张图片

2.6.5 交换 method implementation

Objective-C Runtime基础知识_第41张图片

2.6.6 替换 method

2.6.7 Example

Objective-C Runtime基础知识_第42张图片

下图是开源库 Aspect 上的,每个步骤我都加上注释,对应着上面几个方法的使用:

Objective-C Runtime基础知识_第43张图片

2.7 Selector 相关

2.7.1 获取 SEL 名字

Objective-C Runtime基础知识_第44张图片

2.7.2 判断 SEL 是否相等

Objective-C Runtime基础知识_第45张图片

2.7.3 将 SEL注册到 runtime

Objective-C Runtime基础知识_第46张图片

2.8 IMP 相关

2.8.1 通过一个 block 获取 IMP

Objective-C Runtime基础知识_第47张图片

2.8.2 通过一个 IMP 获取 block

Objective-C Runtime基础知识_第48张图片

2.8.3 删除 IMP

Objective-C Runtime基础知识_第49张图片

上面使用了 block构建 IMP, 并且使用结束后将 IMP 里的 block 删掉。

Objective-C Runtime基础知识_第50张图片

2.9 property 相关

2.9.1 get Name

Objective-C Runtime基础知识_第51张图片

2.9.2 get attributes

2.9.3 copy attribute list

Objective-C Runtime基础知识_第52张图片

2.9.4 copy attribute value

2.9.5 Example

Objective-C Runtime基础知识_第53张图片

打印了RLAccount 的属性的 attribute,name 为 T, &, N, V, R等等, T 为类型,V对应于值为,编译器自动生成的变量,eg: _age, _name。

Objective-C Runtime基础知识_第54张图片

苹果文档上的定义:

Objective-C Runtime基础知识_第55张图片

2.10 protocol 相关

2.10.1 get protocol name 【 获取协议的名字 】

Objective-C Runtime基础知识_第56张图片

2.10.2 get protocol 【 通过名字获取协议结构 】

Objective-C Runtime基础知识_第57张图片

2.10.3 判断两个协议是否相等

Objective-C Runtime基础知识_第58张图片

2.10.4 一个协议是否采用另一个协议

Objective-C Runtime基础知识_第59张图片

2.10.5 获取 runtime 上所有协议结构

Objective-C Runtime基础知识_第60张图片

2.11 associated reference 相关

2.11.1 set associated object 【 设置关联对象 】

Objective-C Runtime基础知识_第61张图片

2.11.2 association policy 【 设置关联对象的属性:assign, retain, copy 】

Objective-C Runtime基础知识_第62张图片

2.11.3 get associated object 【 获取关联对象 】

Objective-C Runtime基础知识_第63张图片

2.11.4 remove associated objects 【 删除对象中的所有关联对象 】

Objective-C Runtime基础知识_第64张图片

2.11. 5 Example:

下图是开源库Aspect 库上的代码:

以 aliasSelector 作为 Key, 获取NSObject 对象的关联对象,如果没有,则新建一个 AspectsContainer 对象,并且设置为关联对象,使用 OBJC_ASSOCIATION_RETAIN, 原子强引用标识。

Objective-C Runtime基础知识_第65张图片

下图是开源库 Mantle上的代码。获取键值集合,如果没有设置,则遍历类对象以及其父类的属性列表,如果没有加入到keys 中,则添加进来。最后设置keys 为关联对象。在 《Mantle 源码分析》上详细讲解相关内容。

源码链接:SwiftZimu/RuntimeLearning

Objective-C Runtime基础知识_第66张图片

你可能感兴趣的:(Objective-C Runtime基础知识)