美文网首页iOS底层原理
Objective-C对象的本质

Objective-C对象的本质

作者: 爱看书de图图 | 来源:发表于2020-09-16 14:12 被阅读0次

      我们知道平时编写的OC代码,底层都是CC++代码:Objective-C -> C/C++ -> 汇编语言 -> 机器语言。所以Objective-C对象的底层实现就是CC++
      那么请思考问题:Objective-C对象,是基于C/C++什么类型的数据结构实现的?
      答案大家都知道,结构体。因为结构体可以存放不同的数据类型。想要看清OC的底层实现,我们可以使用下面①的命令行。但是编译后我们会发现,一个简单的文件就会编译出超过10万行的代码,这是因为clang在编译时候,是有不同的平台区分的,比如Windows平台,Mac平台。所以我们可以使用②的命令指定平台,这样代码就会简化很多。

    1、clang -rewrite-objc main.m -o main.cpp
    2、xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

      我们可以看找到一个类对象的实现,NSObject_IMPLNSObject implementation

    struct NSObject_IMPL {
        Class isa;
    };
    

      然后我们在定义一个类Student然后继续执行上面的命令,看看在底层转化是否是结构体如下所示。这里是Student_IMPL结构体的第一个成员变量就是NSObject。这样的好处就是我定义的Student类里拥有NSObject的所有属性。

    struct Student_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
    };
    

    下面有个问题,为什么NSObjectisaClass类型的,通过之前的iOS开发之alloc和init分析,我们查看方法initInstanceIsa,我们可以看到isa的类型是isa_t(如下图isa_t),在底层进行了强转,类似于swift中的as,可以查看源码(如下图强转)。

    isa_t 强转
    总结:

    1.OC的本质是结构体
    2.子类的isa继承自父类

    isa_t的类型
    isa_t
      在这里我们看到了一个关键词union(共用体/联合体),构造数据类型的方式,有两种:

    1、结构体(struct)。
    2、联合体(union,或者叫共用体)。

    结构体
    结构体是指把不同的数据组合成一个整体,其变量是共存的,变量不管是否使用,都会分配内存。

    • 缺点:所有属性都分配内存,比较浪费内存,假设有4个int成员,一共分配了16字节的内存,但是在使用时,其实使用四个字节就可以表示。那么剩下的12个字节就属于浪费。
    • 优点:存储容量较大,包容性强,且成员之间不会相互影响

    联合体
    联合体也是由不同的数据类型组成,但其变量是互斥的,所有的成员共占一段内存。而且共用体采用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会将原来成员的值覆盖掉

    • 缺点:包容性弱
    • 优点:所有成员共用一段内存,使内存的使用更为精细灵活,同时也节省了内存空间

    两者的区别
    内存占用情况

    • 结构体的各个成员会占用不同的内存,互相之间没有影响
    • 共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员

    内存分配大小

    • 结构体内存 >= 所有成员占用的内存总和(成员之间可能会有缝隙)
    • 共用体占用的内存等于最大的成员占用的内存
        我们继续回到isa_t的分析中,我们看内部实现,发现有两个变量,clsbits,上面我们讲述了共用体的特点,所以我们知道,isa的初始化方式有两种:一、通过cls初始化,那么bits没有默认值。二、通过bits初始化,cls没有默认值。而且详细的列除了isa的位域信息ISA_BITFIELD(如下图)。接下来,我们详细分析这些信息所代表的含义。 位域

    1.nonpointer

    • 0:纯isa指针
    • 1:不只是类对象地址,包含了类信息,引用计数等。

    2.has_assoc表示是否有关联对象。
    3.has_cxx_dtor表示该对象是否有C++/OC的析构函数。

    • 如果有析构函数,则需要做析构逻辑。
    • 如果没有,则可以更快地释放对象。

    4.shiftclx表示存储类信息。arm64架构下33位,x86_64架构下占44位。
    5.magic用于调试器判断当前对象是真的对象还是没有初始化的空间,占6位。
    6.weakly_refrenced,是否被弱引用,没有可以更快释放。
    7.deallocating表示对象是否正在释放内存。
    8.has_sidetable_rc表示当对象引用计数大于10时,则需要借用该变量存储进位。
    9.extra_rc(额外的引用计数) --- 导尿管表示该对象的引用计数值,实际上是引用计数值减1。如果对象的引用计数是10,那么extra_rc为9.

    验证isa指针位域信息(0-64) image.png

      上方的nonpointertrue,所以走下面的方法,进入第一个断点,我们进行lldb调试,返现newisa信息都为空,继续往下走,当走到newisa.bits = ISA_MAGIC_VALUE; define ISA_MAGIC_VALUE 0x001d800000000001ULL后,我们在进行调试(如下图)


      和上面的信息比较,我们得到了cls = 0x001d800000000001,在计算器中打开(如下图)   我们发现从47位开始是111011,然后我们把计算器转换成10进制,输入magic = 59(如下图),同样的也是111011,在47号位置的值就是59。有没有感觉到很神奇,666!!!
      所以calloc的意义和我们之前文章里讲述的就一模一样了,是把isa和我们的类,关联起来。newisa.shiftcls = (uintptr_t)cls >> 3;,我们来进一步验证这行代码。经过赋值之后,我们看到newisashiftcls被赋值成功了,注意,我们上面是没有shiftcls的。右移三位是为了不覆盖原来的前三个位置的信息(nonpointer,has_assoc,has_cxx_dtor

    相关文章

      网友评论

        本文标题:Objective-C对象的本质

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