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 数据结构。

我们需要了解 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是函数指针。

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

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

【图片来自网络】

所有的 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

method_count 方法个数

space 占用空间

method_list 可变长度的 objc_method 结构数组

1.3 objc_ivar

ivar_name 变量名

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

ivar_offset 基地址偏移字节

1.4 objc_ivar_list

ivar_count 变量个数

space 占用空间

ivar_list 可变长度的 objc_ivar 结构数组

1.5 objc_category

category_name 类别的名称

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

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 】

2.1.3 类的版本信息 version 【 getVersion 】

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

2.1.5 Example

2.2 获取 ivarList, methodList, propertyList, protocolList

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

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

2.2.2 方法列表 method list 【 copyMethodList 】

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

2.2.3 属性列表 property list 【 copyPropertyList 】

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

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

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

2.3 添加 ivar, method, property, protocol

2.3.1 添加成员变量 【 add Ivar 】

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

2.3.2 添加方法 【 add Method 】

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

2.3.3 添加属性 【 add Property 】

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

2.3.4 添加协议 【 add Protocol 】

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

2.4 添加 class

2.4.1 创建一个新类 alloc class pair

2.4.2 注册类 register class pair

2.4.3 释放创建的类 dispose class pair

2.4.4 Example:

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

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

2.5 ivar 相关

2.5.1 ivar name 【 变量名 】

2.5.2 ivar type encoding 【 变量类型 】

2.6 method 相关

2.6.1 method name 【 方法名 】

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

2.6.3 获取 method implementation

2.6.4 设置 method implementation

2.6.5 交换 method implementation

2.6.6 替换 method

2.6.7 Example

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

2.7 Selector 相关

2.7.1 获取 SEL 名字

2.7.2 判断 SEL 是否相等

2.7.3 将 SEL注册到 runtime

2.8 IMP 相关

2.8.1 通过一个 block 获取 IMP

2.8.2 通过一个 IMP 获取 block

2.8.3 删除 IMP

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

2.9 property 相关

2.9.1 get Name

2.9.2 get attributes

2.9.3 copy attribute list

2.9.4 copy attribute value

2.9.5 Example

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

苹果文档上的定义:

2.10 protocol 相关

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

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

2.10.3 判断两个协议是否相等

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

2.10.5 获取 runtime 上所有协议结构

2.11 associated reference 相关

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

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

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

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

2.11. 5 Example:

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

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

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

源码链接:SwiftZimu/RuntimeLearning

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容