Objective-C isa 指针 与 runtime 机制 这篇文章中详述了OC中Runtime的结构,附下图,
每个 类 都有一对儿objc_class结构体:存储实例相关内容的结构体和存储类相关内容的结构体(meta),
方便起见姑且把前者称之为Oclass,后者称之为Mclass吧,Oclass中的isa指向Mclass
这里只是简单介绍下就不多做赘述,重点要说的是Instance of class的结构,也算是对那篇文章的一个补充,
那篇文章中介绍了OC中Runtime结构组成,但没有说明创建实例时的结构和实例变量存储在哪里,
如上图,创建一个实例时,只创建一个Instance(objc_object结构体),然后其中isa指针指向系统中唯一的一对儿objc_class,但是我们可以看到objc_object结构体中只有一个isa指针,那么它的实例变量存储在那里呢?下面会详细介绍
下面是源码中的objc_class该结构体:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
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;
} OBJC2_UNAVAILABLE;
每alloc一个实例的时候,会生成一个objc_object结构体如下,其中isa指向Oclass,Object就是一个objc_object
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
所有的实例方法地址都存储在Oclass的methodLists中,类方法地址在Mclass的methodLists中,调用实例内容就是在Oclass中寻径,调用类内容则在Mclass中寻径。
现在要说最关键的了:实例所拥有的变量存储在哪里呢?(这个问题官档没细说,其它介绍Runtime的文章也没提)
因为每个类在运行时只存在唯一一对儿Oclass和Mclass,可以看到objc_class中有个ivars,好像是存这里,但是实例变量是每个实例自有的啊!难道所有的objc_object的的实例变量都存在系统当中唯一的Oclass中吗?这是不合理的,讲道理的话实例变量应该存在objc_object当中才对,但是objc_object中只有一个isa指针,并没有地方去存变量。
源码里可以发现,ivars中存的是一个叫偏移量的东西,这个偏移量记载了实例变量存储的位置相对于objc_object的偏移量。所以获取实例变量的过程就是,先通过objc_object的isa指针找到Oclass,然后获取ivars中该实例变量存储位置相对于objc_object存储地址的偏移量,然后沿着objc_object的地址偏移个位置找到该实例。
这样的结构就解决了唯一一对儿objc-class对应无数个objc-object的问题。
但同样带了的缺陷就是开创一个objc-object时,就要对它在内存中的布局安排好,为它后面的实例变量预留空间,如果这个实例的某些变量压根儿没用到,即使不被赋值,它依旧占用着空间。(不知道apple有没有对这个做一些类似内存映射的技巧优化解决这个问题)。