iOS runtime 类与对象

Objective-C runtime 是一个运行时库,它提供支持 Objective-C 语言的动态属性,其优势在于程序正在运行时,我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等;

Runtime 库主要做下面几件事:

  • 封装:在这个库中,对象可以用 C 语言中的结构体表示,而方法可以用 C 函数来实现,另外再加上了一些额外的特性。这些结构体和函数被 runtime 函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。

  • 找出方法的最终执行代码:当程序执行 [object doSomething] 时,会向消息接收者 (object) 发送一条消息 (doSomething),runtime 会根据消息接收者是否能响应该消息而做出不同的反应,我们会在后面系列详细介绍。

要了解 runtime 的基本工作原理,,我们先来了解一下类与对象,这是面向对象的基础,我们看看在Runtime中,类和对象是如何实现的。

Class

Objective-C 类是由 Class 类型表示,它是一个指向 objc_class 结构体的指针。它的定义如下:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

objc_class 结构体定义如下:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

我们来看下其中每个字段的含义:

  • isa: 在 OC 中,任何数据结构只要在恰当的位置具有一个指针 isa 指向一个 Class,都可以被认为是一个对象。从这里可以看到类自身也是一个对象,我们称为类对象,这个 isa 指针指向 metaClass(元类),我们会在后面介绍它。

  • super_class: 指向该类的父类,如果该类已经是最顶层的根类(如 NSObject 或 NSProxy),则 super_class 为 NULL。

  • name: 类名

  • version:类的版本信息

  • info:类信息,供运行期使用的一些位标识。

  • instance_size:该类实例变量大小

  • ivars:该类的成员变量链表

  • methodLists:方法定义的链表,实例方法从这里查找,而类方法从这个类的 meta-class 的方法列表中查找。

  • cache:方法缓存。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率。

  • protocols:协议链表

id

id 是指向 objc_object 的指针,定义如下:

/// A pointer to an instance of a class.
typedef struct objc_object *id;

objc_object 结构体定义如下:

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

可以看到,这个结构体只有一个字体,即指向其类的 isa 指针。这样,当我们向一个 Objective-C 对象发送消息时,运行时库会根据实例对象的 isa 指针找到这个实例对象所属的类。Runtime 库会在类的方法列表及父类的方法列表中去寻找与消息对应的 selector 指向的方法,找到后即运行这个方法。

Meta Class

meta-class 就是一个类对象的类。举个例子:

NSArray *array = [NSArray array];

其中 NSArray 是类对象,根据 objc_class 定义有一个指针 isa 指向一个 Class,所以 NSArray 这个类本身又是一个对象,我们且称呼为类对象。那么
+array 消息发送给了 NSArray,为了调用 +array 方法,这个类的 isa 指针必须指向一个包含这些类方法的一个 objc_class 结构体,这个结构体就是 meta-class。

当我们向一个对象发送消息时,runtime 会在这个对象所属的这个类的方法列表中查找方法;而向一个类发送消息时,会在这个类的 meta-class 的方法列表中查找。

那么问题来了,meta-class 也是一个类,它的 isa 又指向什么呢?

答:NSObject 类元对象。

那么 NSObject 类元对象的 isa 又指向什么呢?

答:NSObject类元对象自身。

即,任何 NSObject 继承体系下的 meta-class 都使用 NSObject 的 meta-class 作为自己的所属类,而基类的 meta-class 的 isa 指针是指向它自己。这样就形成了一个完美的闭环。如下图所示:

image

写个例子来验证一下:

Class class = [CustomObject class];//类对象

Class metaClass = object_getClass(class);//类元对象
Class metaOfMetaClass = object_getClass(metaClass);//NSObject类元对象
Class rootMataClass = object_getClass(metaOfMetaClass);//NSObject类元对象的类元对象

NSLog(@"CustomObject类对象是:%p",class);
NSLog(@"CustomObject类元对象是:%p",metaClass);
NSLog(@"metaClass类元对象:%p",metaOfMetaClass);
NSLog(@"metaOfMetaClass的类元对象的是:%p",rootMataClass);
NSLog(@"NSObject类元对象:%p",object_getClass([NSObject class]));

控制台输出:

CustomObject类对象是:0x10248aed0
CustomObject类元对象是:0x10248aea8
metaClass类元对象:0x102ce5198
metaOfMetaClass的类元对象的是:0x102ce5198
NSObject类元对象:0x102ce5198

SuperClass

在面向对象中,我们知道,子类调用一个方法,如果子类没有实现,会查找基类。OC 作为一种面向对象的语言,当然支持这些。
我们依然写一段示例代码:

Class class = [CustomObject class];//类对象
Class superClass = class_getSuperclass(class);//基类对象NSObject
Class superOfNSObject = class_getSuperclass(superClass);//NSObject类元对象

NSLog(@"CustomObject类对象是:%p,%@",class,class);
NSLog(@"CustomObject类superClass是:%p,%@",superClass,superClass);
NSLog(@"NSObject的superClass是:%p,%@",superOfNSObject, superOfNSObject);

控制台输出:

CustomObject类对象是:0x101792e88, CustomObject
CustomObject类superClass是:0x101fec170,NSObject
NSObject的superClass是:0x0,(null)

由此,我们可以绘制继承关系图:

image

综合上述两个例子,我们绘制出完整的 isa 和 superClass 图:

image

类与对象操作函数

runtime 提供了大量的函数来操作类与对象。类的操作方法大部分是以 class_ 为前缀的,而对象的操作方法大部分是以 objc_ 或o bject_ 为前缀。

我们可以回过头去看看 objc_class 的定义,runtime提供的操作类的方法主要就是针对这个结构体中的各个字段的。

相关 API 使用请看这里:Objective-C Runtime Reference

小结

本章主要介绍了 Runtime 中类和对象的相关结构,对 Object-C 底层面向对象实现有更深一步的理解。

参考资料:

runtime 源码

Objective-C Runtime Reference

Objective-C Runtime Programming Guide

南峰子技术博客 http://southpeak.github.io/categories/objectivec/

https://blog.csdn.net/Hello_Hwc/article/details/49687543

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

推荐阅读更多精彩内容