在上一篇的文章 iOS alloc 流程分析 中我们分析了 alloc ,知道了 alloc 创建了对象并且分配内存,同时初始化 isa 属性。我们也知道了 Objective-C 对象在底层本质上是结构体 ,所有的对象里面都会包含有一个 isa ,这篇文章我们来分析探究 isa底层是如何实现的 。
其中的 nonpointer 字面的意思是没有指针的,一般情况下nonpointer是为true的,只有在例如实现了 allocwithzone 方法,retain,release等的时候会是false。如果为false是直接将传进来的cls为isa的关联的cls赋值。
其他的剩下的部分就是对isa的初始化赋值了。但是具体的isa内部是怎样的还是不知道的,从源码中 isa_t 点击进去可以查看。
通过源码可以知道 isa 的 isa_t 类型的内部结构
从中可以知道, isa 是一个 联合体 union ,里面有关联的类cls和long型的bits。
联合体(union)、位域不清楚的可以参考 这篇文章
由上面的概念可以知道, cls 和 bits 之间是互斥的,即有cls就没有bits,有bits就没有cls。这就很好地解释了为什么上面的源码在初始化isa的时候会用nonpointer来区分开。所以isa的大小占8个字节,64位。其中这64位中分别存储了什么呢?通过ISA_BITFIELD位域源码:
这两种是分别 arm64 和 x86 系统架构下isa的内部结构,但是都是64位的。iPhone 采用 arm64 架构,MacOS 采用 x86 架构,本文介绍 x86 架构下 isa的联合体结构 。
通过 objc4-781 苹果官方的源码,使用object_getClass这个方法获取到类。
通过源码找到object_getClass的方法
从源码中可以知道返回的 isa 最终是 (Class)(isa.bits &ISA_MASK)
其中源码有一个判断 isTaggedPointer() ,其中苹果对于 Tagged Pointer 的概念引入,是为了节省内存和提高执行效率,对于 64 位程序,相关逻辑能减少一半的内存占用,以及 3 倍的访问速度提升,100 倍的创建、销毁速度提升。如果想了解这部分的内容可以看看 深入理解 Tagged Pointer
下面就是用 lldb 的指令来验证一下的。通过 x/4gx objc 打印出对象的内存地址
由源码知道类Class的最终返回是 (Class)(isa.bits &ISA_MASK) ,所以将 x/4gx objc 打印出来的 0x001d800100002119 &ISA_MASK 的值得到如下:
由前面的文章知道由于内存的优化对象的其他属性的位置实际会发生变化的,所以对象的第一个属性就是 isa
通过测试我们发现 对象的内存地址 和通过 isa取出来的内存地址 是一样的,所以 isa 是关联着对象与类的
通过上面的介绍,可以知道了isa是关联着对象与类的,并且对象的isa指向类,因为万物皆对象,那么类的isa指向的是谁呢?可以通过苹果官方的isa的走位流程图:
其中虚线是代表 isa 的走位,实线代表的是 继承关系 的走位。
通过以上的源码分析,我们认识到对象的 isa指针 指向了对象所属的类。而类本身也有一个 isa指针 ,它指向的又是什么呢?
此时要引入 meta class (即元类)的概念了。我们先了解一下元类的信息:
验证过程见参考文章
具体思路是, shiftcls 在 x86_64 架构下长度是44位,存储在 isa 的 [3, 46]位上,所以可以通过将isa的 [0, 2]位、[47, 63]位清零,同样能得到 shiftcls 的值,进而确定类。(先右移3位、再左移20位、然后右移17位即可)
如果 Person 类继承的是 NSProxy ,相关 isa 指向是怎样的呢?
答案:跟 NSObject 一样,两者都是 根类 。
OC源码分析之isa
iOS的OC的isa的底层原理
多年IOS开发,林林总总用过不少系统SDK框架,最近打算做一个大总结,系统性的整理下各种框架以及功能。
这篇算是开端,先整体介绍下IOS的底层,闲话少说切入正题
整个IOS系统架构大致分为上面四个层
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)