美文网首页
iOS底层-对象的本质

iOS底层-对象的本质

作者: 沉淀纷飞 | 来源:发表于2021-06-16 18:20 被阅读0次

前言

      作为一名iOS开发者,了解底层源码实现有助于在开发中更容易的来解决一些异常的问题,而且有助于面试题的解答。对象的本质是啥呢?\color{red}{isa}又是啥呢?带着这些疑问我们一起从OC底层源码出发探索它们。本文纯属个人观点,欢迎大家阅读指正,也希望对大家的学习有所帮助。

正文

      首先我们需要清楚编译器是什么,汇编又是什么?简单的说编译器就是把我们的高级编程语言通过一系列操作之后生成了计算机可以执行的机器语言。编译的过程又分为:源代码--预处理器--编译器--汇编程序--目标代码--链接器--可执行文件。这个过程有两个最关键的东西\color{red}{Clang}\color{red}{LLVM}。下面我们通过\color{red}{Clang}将OC代码转变为C++代码看看OC的一些底层知识。

1、创建一个工程项目,使用终端cd到工程文件

2、选择一个想要转换的文件比如main.m 文件,输入命令  \color{red}{xcrun -sdk iphoneos   clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp}

结果:执行完终端命令后我们会发现工程文件里面会多了一个\color{red}{.cpp}的文件,打开此文件

      通过源码可以看到\color{red}{Person}对象是一个结构体\color{red}{objc\_object},而结构体内部第一个变量是一个\color{red}{isa},那么\color{red}{isa}是啥呢,不难发现它是一个结构体指针。下面我们来详细探讨下。

在上一篇文章alloc 底层原理探索中我们其实已经探讨过对象的初始化过程,最终得到的是\color{red}{initInstanceIsa(cls, hasCxxDtor)},其本质也是初始化\color{red}{isa}\color{red}{initInstanceIsa(cls, hasCxxDtor)}代码实现如下

源码中\color{red}{Tagged} \color{red}{Pointer} 是什么呢?

        苹果在WWDC2013提出\color{red}{Tagged} \color{red}{Pointer}专门用来存储小的对象,例如\color{red}{NSNumber}\color{red}{NSDate}\color{red}{Tagged} \color{red}{Pointer}指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要 malloc 和 free;在内存读取上有着 3 倍的效率,创建时比以前快 106 倍。苹果引入\color{red}{Tagged} \color{red}{Pointer},不但减少了 64 位机器下程序的内存占用,还提高了运行效率。完美地解决了小内存对象在存储和访问效率上的问题。

        通过源码不难发现\color{red}{isa}的类型是\color{red}{isa\_t}\color{red}{isa\_t}是一个\color{red}{union},即联合体。联合体和结构体又有什么区别呢?

      1、二者都包含多个不同类型的数据,如\color{red}{int}\color{red}{double}\color{red}{Class}等;

      2、在\color{red}{struct}中各成员有各自的内存空间,一个\color{red}{struct}变量的内存总长度大于或等于各成员内存长度之和,而在\color{red}{union}中,各成员变量共享一段内存空间,一个\color{red}{union}变量的内存总长度等于各成员中内存最长的那个成员的内存长度;

      3、对\color{red}{struct}中的成员进行赋值,不会影响其他成员的值;对\color{red}{union}中的成员赋值时,每次只能给一个成员赋值,同时其它成员的值也就不存在了。

        \color{red}{isa\_t}中有个很重要的成员变量\color{red}{bits}\color{red}{bits}的类型是\color{red}{uintptr\_t}\color{red}{uintptr\_t}实质上是\color{red}{unsigned}\color{red}{long},在arm64架构下\color{red}{bits}长度为8个字节,其各位的存储使用了\color{red}{ISA\_BITFIELD}(位域)。\color{red}{ISA\_BITFIELD}是一种数据结构,可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。所以\color{red}{ISA\_BITFIELD}可以节省储存空间,也很方便的访问一个整数值的部分内容从而可以简化程序源代码。下面我们看下\color{red}{ISA\_BITFIELD}的源码,以arm64架构为例。

    在64位架构下,\color{red}{isa}的长度为8字节,苹果为了优化性能只是用了一部分位,多余的位用于存储其它信息。下面分析一下\color{red}{ISA\_BITFIELD}位域各成员的作用:

\color{red}{nonpointer}:表示是否对\color{red}{isa}指针 开启指针优化。0:不优化,是纯\color{red}{isa}指针,当访问\color{red}{isa}指针时,直接返回其成员变量\color{red}{cls}。1:优化,即\color{red}{isa}指针内容不止是类地址,还包含了类的一些信息、对象的引用计数等。

\color{red}{has\_assoc}:是否有关联对象。

\color{red}{has\_cxx\_dtor}:该对象是否有C++或Objc的析构器。如果有析构函数,则需要做一些析构的逻辑处理,如果没有,则可以更快的释放对象。

\color{red}{shiftcls}:存储类地址。开启指针优化的情况下,在 x86_64位 架构有 44位 用来存储类地址,arm64 架构中有 33位 。

\color{red}{magic}:用于调试器判断当前对象是真的对象,还是一段没有初始化的空间。

\color{red}{weakly\_referenced}:用于标识对象是否被指向或者曾经被指向一个\color{red}{ARC}的弱变量,没有弱引用的对象释放的更快。

\color{red}{deallocating}:标识对象是否正在释放内存。

\color{red}{has\_sidetable\_rc}:对象的引用计数值是否有进位。

\color{red}{extra\_rc}:表示该对象的引用计数值。\color{red}{extra\_rc}只是存储了额外的引用计数,实际的引用计数公式:实际引用计数 =\color{red}{extra\_rc} + 1。这里占了8位,所以理论上可以存储的最大引用计数是:2^8 - 1 + 1 = 256(arm64架构下的\color{red}{extra\_rc}占19位,可存储的最大引用计数为2^19 - 1 + 1 = 524288)。与\color{red}{has\_sidetable\_rc}的关联:当对象的最大引用计数超过界限后,\color{red}{has\_sidetable\_rc}的值为1,否则为0。

      \color{red}{isa\_t}中还有个很重要的成员变量\color{red}{cls}\color{red}{cls}的类型是\color{red}{Class}。通过源码分析\color{red}{Class}\color{red}{objc\_class}的指针变量。\color{red}{objc\_class}的父类是\color{red}{objc\_object},总结:类的本质也是一个对象。因此\color{red}{Class}这个结构体指针变量的值内部有一个\color{red}{isa}成员变量,这个\color{red}{isa}成员变量在64位架构下是8字节,且排在\color{red}{objc\_class}结构体的前8字节。

      我们知道\color{red}{shiftcls}存储的是类地址。在64位架构下,\color{red}{shiftcls}占用44位,也就是第3~46位。将 3~46位 全部填充1,0~2位 和 47~63位 都补0,得到\color{red}{0x7ffffffffff8},也就是\color{red}{ISA\_MASK}的值。所以,\color{red}{isa}\color{red}{\&}\color{red}{ISA\_MASK}会得到\color{red}{shiftcls}存储的类地址。

      下面我们通过案例使用\color{red}{lldb}分析\color{red}{isa}的作用

        这里我们需要了解\color{red}{lldb}的指令x是读取内存的命令,\color{red}{x/4gx}中第一个x是读取内存命令,后面的\color{red}{g}是每次读取8字节,\color{red}{x}的意思是16进制显示结果,\color{red}{4}表示连续打印4段。更多的\color{red}{lldb}调试技巧可查阅lldb常用命令与调试技巧

        观察打印结果发现,0x0000000102e5d690是p的\color{red}{isa}值。通过\color{red}{p/x }0x0000000102e5d690 & 0x00007ffffffffff8即\color{red}{isa \& ISA\_MASK}运算得到的是\color{red}{Person}的内存地址。

          总结\color{red}{isa}将对象和类关联起来,起到了中间桥梁的关联作用

相关文章

网友评论

      本文标题:iOS底层-对象的本质

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