ObjC中的类和实例对象

请注意,本文编写于 392 天前,最后修改于 191 天前,其中某些信息可能已经过时。

NSObject

OC中类的本质是一个结构体

NSObject
NSObject

NSObject类中存在一个Class类型的isa指针。 我们在Xcode编写一个类继承于NSObject,在terminal使用 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xx.m -o xx.cpp 将.m文件转成.cpp文件,窥探NSObject的底层实现。

NSObject的底层实现
NSObject的底层实现

我们发现NSObject类最后转成了一个通过typedef struct objc_object NSObject定义的结构体,这大抵就是NSObject的底层实现了。我们在OC中定义的一个类就是一个C语言的结构体。

有继承关系的类

定义一个Student继承自Person类,声明一个成员变量int No;,依然使用上述命令将其转成cpp文件,窥探Student的底层实现。

Student
Student

Student底层实现
Student底层实现

其中student结构体中存在一个Person_IMPL的结构体,这就是Student的父类。由于我在Person类中声明了一个成员变量age,所以下面是Person_IMPL的结构体的定义。

<code>Person_IMPL</code>的结构体
Person_IMPL的结构体
Person_IMPL的结构体">

而 Person中又存在一个NSObject_IMPL结构体,这是因为Person继承于NSObject。关于NSObject_IMPL在上面已经介绍过,不做赘述。上述关系可以通过下图来表示。

继承关系
继承关系

ObjC对象的分类

OC中的对象主要可以分为实例对象、类对象、元类对象三种。

  • instance对象(实例对象)

instance对象就是通过类alloc出来的。实例对象中存储着一个isa指针和一些成员变量

  • class对象 (类对象)

每个类有且只有一个类对象,class对象中存放着一个isa指针,一个superclass指针,类的属性信息,类的对象方法信息,类的协议方法信息,类的成员变量信息等,其本质是一个objc_class的结构体。

  • meta-class对象 (元类对象)

每个类有且只有一个元类对象,元类对象的结构跟类对象是一样只不过用途不一样。可以通过runtime的class_isMetaClass来验证某个类是不是元类,其本质是一个objc_class的结构体。

isa和superclass

isa和superclass
isa和superclass

上图我们可以看出:

  • instance的isa指针指向class,class的isa指针指向meta-class,metaclass的isa指针指向root-class
  • subclass的superclass指针指向superclass,依次直到root-class,root-class的superclass指针为nil。meta-class的superclass指针指向其superclass的meta-class,依次到root-class,root-class的superclass指向rootclass的class。
  • 而subclass和superclass的isa以及meta-class的isa指针皆指向meta-class的root-class。
  • instance调用实例方法的轨迹:通过isa找class,找不到就通过superclass找父类。
  • class调用类对象的轨迹:通过isa找meta-class,找不到就通过superclass找父类。

Class对象分析

在对象分类的类对象和元类对象中已经描述过其中存放的数据,并且说明元类中存放着和类对象一样结构的数据。

ObjC2以前的类信息
ObjC2以前的类信息

上述是在.cpp文件中找到的类结构体的定义,但是在条件编译的时候已经明确指出,在ObjC2已经不可用了。于是在runtime源码中有这样一个新的定义。

新的ObjC定义
新的ObjC定义

这是一个c++的结构体,他继承自objc_object,objc_object中有一个isa的成员变量和一些方法。

objc_object
objc_object

所以objc_class中存在一个isa,superclass指针,以及方法的缓存和类信息数据(bits)。第一个方法class_rw_t *data()中调用了bits的data()函数,返回了一个class_rw_t类型的结构体。

class_rw_t结构体
class_rw_t结构体

其中存放有方法列表、属性列表、协议列表等,其中还有一个class_ro_t的结构体,里面存放了类的基本信息,包括实例的大小,类名等。

class_ro_t
class_ro_t

验证class

我们知道了类的本质和结构,那么我们可以自己定义一个结构体,里面有跟类一样的变量,将我们的类转换成这个结构体,来验证class的结构。

使用MJ大大编写的MJClassInfo,定义两个类,Person(一个成员变量、一个属性、一个类方法、一个实例方法),Student(一个成员变量、一个类方法、一个实例方法,一个协议),将其转换成相应的结构体类型,这里面是mj_objc_class。

窥探class
窥探class

Person类对象信息
Person类对象信息

我们可以看出定义的实例方法放在method中,属性放在properties中,成员变量在ro的ivars中...

在student中遵守了一个协议,协议放在类的协议列表中:

student类对象
student类对象

我们可以看到protocols的count为1,而person没有遵守协议所以其protocols的count为0。

使用MJClassInfo中的Meta()方法获取元类并调用元类结构体的data()方法窥探元类的结构:

meta-class结构
meta-class结构

我们可以看出metaclass的结构与class 的结构一样,只不过存储的数据不一样。我们定义的类方法存储在metaclass的method中。

两个问题

  1. 一个NSObject* obj = [[NSObject alloc] init];实例对象占多少内存空间?

    答:由于NSObject实例对象就是C语言中的结构体,而这个结构体中仅有一个isa指针,所以其使用的内存空间就是一个指针的内存空间。系统为NSObject对象分配了16个字节的空间,但是在64位系统中实际使用的只有8个字节,32位系统中实际使用的是4个字节。
    

2.对象的isa指针指向哪里?

答:通过isa和superclass的图例可以看出实例对象的isa指针指向类对象,类对象的isa指针指向其元类对象,元类对象的isa指针你指向基类。而子类和父类的isa以及元类的isa指针皆指向元类的基类。

Comments

添加新评论