对于Objective-C runtime的浅薄认知(一)

其实我也不知道怎么开头, 那么就直接进入正题吧, runtime是Objective-C动态语言特性的体现, 在实际编码的过程中我们运用最多的就是category中动态创建属性, Method Swizzling, 下面我们从类和对象开始一步一步的了解runtime。

屏幕快照 2016-06-08 上午11.49.11.png

上面这段话出自Objective-C Runtime Programming Guide, 对于runtime, 有两个版本, 一个叫做“modern”, 一个叫做“legacy”。“modern”版本是以OC 2.0为基础,包括许多新的runtime特性;而“legacy”版本则是涉及OC 1为基础, 我们可以不必理会。

类和对象的基础数据结构

Class 类

屏幕快照 2016-06-08 下午2.12.21.png

首先, 我们来分析一下Class的结构体定义
1.isa: 在OC中, 类也是对象, 而既然是对象, 则该对象的isa指针指向的地址则是metaClass(元类), 对于metaClass, 下面会有图来介绍, 这里理解类是对象。
2.super_class:指向该类的父类, 当该类已经是NSObject等最顶层的类, 那么该类的super_class为NULL。
3.name:类名,及该类的名称, 如创建了一个叫MyClass类继承NSObject, 则该类的name为MyClass。
4.instance_size:该类实例变量的大小, 可以通过调用class_getInstanceSize(Class cls)方法来得到其值。
5.ivars:实例变量列表, 该列表以链表的形式返回,通过节点找到对应的实例变量所在的位置。
6.methodLists:方法链表, 包括类方法和对象方法。
7.cache:顾名思义, cache是缓存的意思, 但是此处的cache是对方法的缓存, 当接收者接收到一个消息是, 首先会根据isa指针去寻找能够响应该方法的对象, 当调用该方法后, 调用的方法会缓存到cache中, 下次调用的时候会先从cache中查找该方法, 如果该方法不存在, 再去methodLists中查找方法, 这样大大的提高的程序运行的效率。

例如: UILabel *label = [[UILabel alloc] init];
1.创建label对象, 首先调用+alloc的方法, 因为UILabel没有+alloc方法, 所以去父类UIView中找,没有再到UIResponder, 最后在NSObject中找到了+alloc方法, 之后会给UILabel分配内存空间, 同时+alloc方法存到cache中。
2.之后同样的方法执行init方法, 同样,-init方法被存到cache中。
3.如果再通过该方式创建UILabel对象, 则会先去cache中查找方法, 不必一直去method_lists中查找。

objc_object与id


屏幕快照 2016-06-08 下午3.29.06.png

对于对象来说, 其结构体中只有一个isa指针, 指向其类, 当runtime运行objc_msgSend方法的时候, 去方法列表中寻找能够响应的方法, 运行方法。

当创建一个特定类的实例对象时,分配的内存包含一个objc_object数据结构,然后是类的实例变量的数据。NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。

对于我们常见的id, 它是一个objc_object结构类型的指针, id类型可以转化成任意类型, 类似于C中的void *指针的作用。

元类(Meta Class)


屏幕快照 2016-06-08 下午3.39.18.png

想必了解runtime的人对于这张图应该不陌生了, 这张图很好的解释了meta class。
我们先看Subclass的部分, instance of Subclass, 初始化实例对象, 实例对象及为objc_object结构体, 实例对象的isa指针会指向类, 而上文所介绍的Class结构体中也存在isa指针, 而Class中的isa指针则会指向meta class, 同理, Superclass和Root class的isa指针与Subclass的isa指针类似, 因为元类也是类, 所以元类也存在isa指针, 这是, Subclass和Superclass的isa指针都会指向Root class的meta class, 当然这种循环不能一直下去, 所以Root class的meta class的isa指针会指向自己, 例如所有继承NSObject的类的meta class的isa指针都会指向NSObject的meta class, 而NSObject的meta class的isa指针则会指向自己。值得注意的是, 不管是Subclass还是Superclass, 他们的meta class都是不同的, 即每个类的meta class都不相同。这段可能会比较绕, 天资愚钝, 这张图我也是看了好几十遍才弄懂的, 希望大家能够多想。

下面引用别人写好的一个例子来说明一下,代码中有详细的标注, 我们可以看到打印的结果, 对着结果, 结合上图, 会有更好的理解


屏幕快照 2016-06-09 上午11.24.58.png
屏幕快照 2016-06-09 上午11.30.09.png

类和对象的操作函数
这里我们介绍几个常用的类和对象的操作函数, 如下图:

屏幕快照 2016-06-09 上午11.50.35.png

1.对于class_getName, 传入对应的Class, 则会返回其名称
2.class_getSuperclass(Class cls), 传入对应的Class, 获取其父类
3.class_getInstanceSize(Class cls), 传入对应的Class, 计算出实例的大小
4.class_isMetaClass(Clas cls), 传入对应的Class, 返回的是BOOL类型, 岸段该Class是否为元类
5.class_getInstanceVariable(Class cls, const char *name),获取到指定名称实例成员变量的信息, name为传入的变量名称
6.class_getClassVariable(Class cls, const char *name), 获取类成员变量信息
7.class_copyIvarList(Class cls, unsigned int *outCount), 获取整个成员变量列表
8.objc_getProperty(Class cls, const char *name), 获取指定的属性
9.objc_copyPropertyList(Class cls, unsigned int *outCount), 获取属性列表
注意:这里outCount参数返回的是个数, 在获取玩IvarList和PropertyList以及我们没有提到的MethodList后要使用free()方法来释放。具体的对于类和对象操作的函数我会在代码中给出, 大家可以对照代码, 动手操一番

动态创建类和对象
1.动态创建类


屏幕快照 2016-06-09 下午8.08.41.png

(1).objc_allocateClassPair函数:如果我们要创建一个根类,则superclass指定为Nil。extraBytes通常指定为0,该参数是分配给类和元类对象尾部的索引ivars的字节数。
为了创建一个新类,我们需要调用objc_allocateClassPair。然后使用诸如class_addMethod,class_addIvar等函数来为新创建的类添加方法、实例变量和属性等。完成这些后,我们需要调用objc_registerClassPair函数来注册类,之后这个新类就可以在程序中使用了。
实例方法和实例变量应该添加到类自身上,而类方法应该添加到类的元类上。
(2).objc_disposeClassPair函数用于销毁一个类,不过需要注意的是,如果程序运行中还存在类或其子类的实例,则不能调用针对类调用该方法, 对于我们需要销毁该类或者子类, 我们需要调用object_dispose(id obj)来销毁已经存在的对象, 注意的是object_dispose(id obj)函数需要在MRC环境下调用, 之后在调用objc_disposeClassPair函数销毁已经常见的类。

屏幕快照 2016-06-09 下午8.16.58.png

<1>我们用objc_allocateClassPair动态创建了一个类,继承自MyClass,MyClass是我之前创建好的类
<2>动态添加叫做subMethod1的方法, 而该方法的IMP指针指向函数imp_submethod1的实现
<3>动态添加实例变量, 叫做_ivar1的NSString类型的实例变量
<4>动态添加叫做Property2的属性
<5>调用objc_registerClassPair注册该类, 需要注意的是, 我们在动态添加方法, 属性, 实例变量的时候一定要在objc_allocateClassPair和objc_registerClassPair两个函数调用之间完成。
<6>创建对象, 调用方法
<7>销毁创建的对象, 销毁创建的类

小结:
这篇只是简单的介绍一下runtime对于类和对象的一些简单的函数, 帮助大家了解Objective-C低层的操作, 同时帮助大家对于元类进行理解, 具体的代码会在下面的地址中给出, 大家可以参考代码对于runtime中的函数有着进一步的了解, 下一篇我们会讲述利用runtime创建property和method swizzling, 让大家在平时的编程中能够用上runtime。

代码下载地址:
https://github.com/CveniEs/Runtime.git
https://github.com/CveniEs/Runtime_Example.git

注:本门的很多内容都参考与南峰子的博客, 有兴趣同学可以去学习一下。
参考:
Objective-C Runtime Programming Guide
Objective-C Runtime Reference
南峰子技术博客

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

推荐阅读更多精彩内容