文章目录
  1. 1. Runtime介绍
  2. 2. 类的定义
    1. 2.1. cache的实现举例
  3. 3. 对象的定义
  4. 4. 类对象
  5. 5. 元类
  6. 6. 代码验证
    1. 6.1. 开源函数
    2. 6.2. 测试代码
  7. 7. 参考

Runtime介绍

Objc Runtime其实是一个Runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。在这个库中,对象可以用C语言中的结构体表示,而方法可以用C函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。

类的定义

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

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

查看objc/runtime.h中objc_class结构体的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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:需要注意的是在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类),我们会在后面介绍它。
  • super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULL。
  • cache:用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。

cache的实现举例

针对cache,我们用下面例子来说明其执行过程:

1
NSArray *array = [[NSArray alloc] init];

其流程是:

  1. [NSArray alloc]先被执行。因为NSArray没有+alloc方法,于是去父类NSObject去查找。
  2. 检测NSObject是否响应+alloc方法,发现响应,于是检测NSArray类,并根据其所需的内存空间大小开始分配内存空间,然后把isa指针指向NSArray类。同时,+alloc也被加进cache列表里面。?加到那个类的cache nsarray or nsobject
  3. 接着,执行-init方法,如果NSArray响应该方法,则直接将其加入cache;如果不响应,则去父类查找。
  4. 在后期的操作中,如果再以[[NSArray alloc] init]这种方式来创建数组,则会直接从cache中取出相应的方法,直接调用。

对象的定义

objc_object是表示一个类的实例的结构体,它的定义如下(objc/objc.h):

1
2
3
4
5
6
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY; //对象所属的类
};

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

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

类对象

在objc/runtime.h可以看到关于objc_class的定义

1
2
3
4
5
6
7
//关于objc_class的定义
struct objc_class : objc_object {
. . .
}

typedef struct objc_class *Class;
typedef struct objc_object *id;

objc_object定义

1
2
3
4
5
6
7
8
9
struct objc_object {
private:
isa_t isa;
···
}

union isa_t {
Class cls;
}

可以看到objc_class继承于objc_object 也是一个对象称为类对象

元类

对象的isa指针指向所属的类,类对象isa指针指向哪里呢。这就引出了元类(meta-class)的概念: 元类(meta-class)是一个类对象的类。meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。

再深入一下,meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。

通过上面的描述,再加上对objc_class结构体中super_class指针的分析,我们就可以描绘出类及相应meta-class类的一个继承体系了,如下图所示:

类和元类的继承关系

代码验证

通过对源码和apple相关文档的阅读可以得到关于isa指针的4个结论:

  1. 对象中的isa指向其所属类的类对象
  2. 类对象的isa指向其所属的元类对象
  3. 类对象的元类对象的isa指向基类(NSObject)的元类对象
  4. 基类(NSObject)的元类对象isa指着指向自身

开源函数

object_getClass返回一个对象所属的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//objc/objc-class.mm
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}

//objc-object.h
inline Class objc_object::getIsa()
{
return ISA();
}

//objc-object.h,
inline Class objc_object::ISA()
{
assert(!isTaggedPointer());
return isa.cls; //isa是一个C语言联合体
}

可以看出object_getClass返回的实际上就是isa联合体中的类元素

通过下面源码可以看出[object class]也是返回对象isa联合体中的类元素,[Class class]返回自己的类对象

1
2
3
4
5
6
7
- (Class)class {
return object_getClass(self);
}

+ (Class)class {
return self;
}

objc_getMetaClass返回类对象的isa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Class objc_getMetaClass(const char *aClassName)
{
Class cls;

if (!aClassName) return Nil;

cls = objc_getClass (aClassName);
if (!cls)
{
_objc_inform ("class `%s' not linked into application", aClassName);
return Nil;
}

return cls->ISA();
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void RTTestMetaClass(id self, SEL _cmd)
{
NSLog(@"the object is %p", self);
NSLog(@"Class is %@, super class is %@", [self class], [self superclass]);

//类对象(当前实例对象的isa指针)
Class currentClass = object_getClass(self);

//NSObject类对象的isa指向(NSObjec的元类)
Class nsObjectClass = object_getClass([NSObject class]);
NSLog(@"object_getClass([NSObject class]) : %p",nsObjectClass);
NSLog(@"objc_getMetaClass(\"NSObject\") : %p",objc_getMetaClass("NSObject"));
NSLog(@"%@(%p) isa -> %p",[NSObject class],[NSObject class],object_getClass(objc_getClass("NSObject")));
NSLog(@"NSObject metaclass isa -> %p",object_getClass(objc_getMetaClass("NSObject")));//元类所属的类

while (currentClass != nsObjectClass) {
NSLog(@"%@(%p) super class :%@(%p)",currentClass,currentClass,class_getSuperclass(currentClass),class_getSuperclass(currentClass));
//打印当前对象的isa指针
NSLog(@"%@(%p)->isa = %@(%p)",currentClass,currentClass,object_getClass(currentClass),object_getClass(currentClass));
currentClass = object_getClass(currentClass);
}
}

打印结果:

RTKnowlegdeSub.m:16 the object is 0x618000073040
RTKnowlegdeSub.m:17 Class is RTKnowlegdeSub, super class is RTKnowledge
RTKnowlegdeSub.m:24 object_getClass([NSObject]) : 0x107954e08
RTKnowlegdeSub.m:25 objc_getMetaClass(“NSObject”) : 0x107954e08
RTKnowlegdeSub.m:26 NSObject(0x107954e58) isa -> 0x107954e08
RTKnowlegdeSub.m:27 NSObject metaclass isa -> 0x107954e08
RTKnowlegdeSub.m:34 RTKnowlegdeSub(0x106fb1580) super class :RTKnowledge(0x106fb1620)
RTKnowlegdeSub.m:35 RTKnowlegdeSub(0x106fb1580)->isa = RTKnowlegdeSub(0x106fb15a8)
RTKnowlegdeSub.m:36 RTKnowledge(0x106fb1620)->isa = RTKnowledge(0x106fb1648)
RTKnowlegdeSub.m:34 RTKnowlegdeSub(0x106fb15a8) super class :RTKnowledge(0x106fb1648)
RTKnowlegdeSub.m:35 RTKnowlegdeSub(0x106fb15a8)->isa = NSObject(0x107954e08)
RTKnowlegdeSub.m:36 RTKnowledge(0x106fb1648)->isa = NSObject(0x107954e08)

可以用一张图来清楚表示上面测试结果

测试结果

参考

[][Objective-C Runtime]Objective-C Runtime

Objective-C Runtime Programming Guide