美文网首页
isa结构分析

isa结构分析

作者: 小糾丶羯 | 来源:发表于2020-09-11 16:43 被阅读0次

在开始分析isa前,我们得先搞清楚一个问题:对象是什么?即对象的本质是什么?要搞清这个问题我们还得先了解一下Clang

一、Clang

1.Clang是什么

Clang是⼀个由Apple主导编写,基于LLVMC/C++/Objective-C编译器。

2.Clang的使用

Clang的使用语法有很多,这里就不一一举例了,有兴趣的可以自行搜索,今天我们用到的语法如下:

//将目标文件编译成c++文件
clang -rewrite-objc main.m -o main.cpp

同时Xcode在安装的时候顺带安装了xcrun命令,xcrun命令在clang的基础上进⾏了⼀些封装,要更好⽤⼀些,用法如下:

//模拟器
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o 
main-arm64.cpp
//手机
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main�arm64.cpp

简单了解完Clang,我们来使用看看吧。

二、对象的本质

1.使用上述clang语法,我们得到了一个cpp文件,如下图:

编译后得到的cpp文件 2.打开并搜索LGPerson,看一下编译前后有什么不同,如下图: 编译前后对比图 3.我们可以看到,编译后LGPerson变成了一个结构体(struct),并且定义的属性name变成了结构体数据成员,同时还多了一个struct NSObject_IMPL NSObject_IVARS,并且也是一个结构体

4.LGPerson继承自NSObject,它在编译后必然也是一个结构体,如下图:

编译后的NSObject 可以看到,这里是将NSObject结构体作为LGPerson第一个数据成员,并且NSObject中有一个isa数据成员,这个属于伪继承,意味着LGPerson拥有NSObject中的所有成员,所以NSObject_IVARS就等效于isa

总结对象的本质其实就是结构体LGPerson中的isa继承自NSObjectisa

三、isa分析

alloc探索中,核心三步中有一个initInstanceIsa方法,进入源码,发现调用的是initIsa方法,如下图:

isa的初始化 可以看到,isa是通过isa_t类型初始化的,那isa_t又是什么类型的呢?带着疑问,我们进一步探索,发现isa_t是一个联合体(union),那联合体又是什么呢?

1.联合体(union)

联合体(union)也叫共用体结构体(struct)一样都是构造数据类型。

结构体

所有变量都是共存的,它们占用不用的内存,优点:容量大成员间互不影响;缺点:不管用不用,都会为成员分配内存,浪费内存

联合体

所有变量都是互斥的,它们共用一段内存,优点:节省内存空间;缺点:包容性弱共用体使用了内存覆盖技术同一时刻只能保存一个成员的值,即如果对新的成员赋值,就会把原来成员的值覆盖掉

2.isa_t

isa_t的结构如下图:

isa_t的结构 可以看到,联合体中定义了两个成员clsbits和一个结构体位域ISA_BITFIELD(用来存放类信息其他信息)。

clsbits说明这里有两种初始化方式:
1.通过cls初始化:即isa的初始化图中的isa = isa_t((uintptr_t)cls)
2.通过bits初始化:即else部分

3.位域

有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。基于这种的数据结构,就是位域

这里ISA_BITFIELD就是一个位域,它有两个版本,分别对应__arm64____x86_64__,即iOSmacOS,如下图:

位域ISA_BITFIELD 1.nonpointer:表示是否对 isa 指针开启指针优化
0:纯isa指针
1:不⽌是类对象地址,isa 中包含了类信息、对象的引⽤计数

2.has_assoc:是否关联对象标志位
0:没有
1:存在

3.has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器
如果析构函数(类似于OC中的dealloc),则需要做析构逻辑,
如果没有,则可以更快的释放对象

4.shiftcls: 存储类指针的值
arm64:开启指针优化的情况下,在 arm64 架构中有33位⽤来存储类指针
x86_64:有44位用来存储类指针

5.magic:⽤于调试器判断当前对象是真的对象还是没有初始化的空间

6.weakly_referenced:对象是否被指向或者曾经指向⼀个ARC的弱变量
没有弱引⽤的对象可以更快释放

7.deallocating:标志对象是否正在释放内存

8.has_sidetable_rc:当对象引⽤技术⼤于10时,则需要借⽤该变量存储进位

9.extra_rc:当表示该对象的引⽤计数值,实际上是引⽤计数值减 1
如果对象的引⽤计数为 10,那么 extra_rc为 9
如果引⽤计数⼤于 10,则需要使⽤到has_sidetable_rc

isa的存储分布如下图:

isa存储分布图

4.分析

我们跟随代码来到initIsa方法中,如下图:

isa的初始化 1.根据nonpointer参数为true,这里执行了else的操作,创建了newisa联合体,打印结果如下图: 赋值前的newisa 2.使用ISA_MAGIC_VALUEbits进行赋值,看一下赋值后的结果,如下图: 赋值后的newisa 通过对比,发现这里给cls赋值0x001d800000000001,因为在给bits赋值时,会为cls追加默认值,但是为什么magic赋值了59呢?

打开计算器并输入0x001d800000000001,从47位开始读取6位,将59二进制,发现都为111011,如下图:

59的由来 可以看出,这里进行了进制转换,将指针地址二进制,并从47位开始读取6位,再转换成十进制。但是为什么是从47开始呢?

根据赋值后的newisa图,前面有4个位域nonpointer占1位,has_assoc占1位,has_cxx_dtor占1位,shiftcls占44位)共占了47位,所以magic47位开始。

3.通过newisa.shiftcls = (uintptr_t)cls >> 3关联类信息。

isa关联到类 可以看到,这里shiftcls已经赋上了右移后得到的值536871986,同时cls也从默认值变成了LGPerson,实现了isa与类的关联

这里需要注意两点:
为什么shiftcls需要强转类型?
因为此时,cls可能是个字符串,直接存储会导致无法识别,所以强转成uintptr_tunsigned long类型)

为什么需要右移3位
因为shiftcls前面还有3个位域,为了避免影响它们,故右移将它们抹零

相关文章

  • Cache_t的结构和原理

    在之间的文章里我们分析了isa的指向和结构isa结构分析,分析了bits类的结构分析,在这篇文章里,我们来分析ob...

  • iOS - isa的初始化&指向分析

    isa结构及初始化分析 什么是isa,首先我们先看一下isa的结构: 由源码我们可以看出:isa的本质就是一个联合...

  • isa结构分析

    什么是对象? 为了了解Objective-C类在底层会编译成什么,我们先新建一个类DebugPerson。 测试类...

  • isa结构分析

    isa结构分析 OC对象的本质 clang命令 把oc类编译成c++文件,对象在底层编译成struct 联合体(共...

  • isa结构分析

    OC对象的本质 在一个内中添加一个私有类,在main.m文件中添加一个LGPerson类 使用clang将main...

  • isa结构分析

    在我们iOS开发进行lldb调试的时候,经常会在控制台看到isa的存在,那么本文就来分析一下isa的结构。 在分析...

  • isa结构分析

    背景 书接上回alloc流程图分析中,在最后calloc分配空间,可得到空间的地址,那么calloc中系统是如何分...

  • ISA结构分析

    了解对象 Objective-C是一门面向对象编程语言。对象是什么,我们这篇文章讲的isa和对象又有什么样的关系呢...

  • isa结构分析

    在之前的文章OC对象的alloc过程中,我们探讨了OC对象初始化的主要过程,在第三步,主要是调用 initInst...

  • isa结构分析

    在对象调用alloc, 之后调用的最后一个方法是obj->initInstanceIsa, 它的作用是将isa指针...

网友评论

      本文标题:isa结构分析

      本文链接:https://www.haomeiwen.com/subject/efupektx.html