美文网首页
iOS-isa结构探一探

iOS-isa结构探一探

作者: 灰溜溜的小王子 | 来源:发表于2020-09-11 16:52 被阅读0次

本文仅记录笔者的学习过程,只代表笔者个人的理解,如果有错的地方,欢迎各位指正!

OC对象的本质

对象继承与NSObject翻看Objc源码可以看到

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

可以看到Class类型的isa继续跟进去可以看到

typedef struct objc_class *Class;

可以得治Class其实是个结构体,继续找objc_class结构体的定义

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

从上面的源码中我们得知对象的本质是个结构体,且结构体中国呢的第一个参数是一个isa

isa是什么

这个isa也就是将要探索的内容。在笔者OC对象之alloc探索-源码探索的三种方式这篇博文中可以看到有这么一个初始化

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;

    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }
    //创建isa
    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}

其中有obj->initInstanceIsa(cls, hasCxxDtor)跟踪这个创建过程

objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa = isa_t((uintptr_t)cls);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());

        isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}
断点定位 image.png
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};
  • 我们可以发现,其实isa_t是一个联合体位域结构,采用这种结构的原因也是基于内存优化的考虑(即二进制中每一位均可表示不同的信息)。
  • 通常来说,isa指针占用的内存大小是8字节,即64位,已经足够存储很多的信息了,这样可以极大的节省内存,以提高性能。
  • isa_t这个粘合体中可以发现内部嵌套了结构体,结构体内部是哥宏定义ISA_BITFIELD
    image.png

至此isa的结构清晰了,我们就来讲解一下,他每一个字段代表的含义吧:

  • nonpointer:表示是否对isa指针开启指针优化,0:纯isa指针,1:不⽌是类对象地址,isa 中包含了类信息、对象的引⽤计数等。
  • has_assoc:关联对象标志位,0没有,1存在。
  • has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象。
  • shiftcls:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位⽤来存储类指针。
  • magic:⽤于调试器判断当前对象是真的对象还是没有初始化的空间。
  • weakly_referenced:志对象是否被指向或者曾经指向⼀个 ARC 的弱变量,没有弱引⽤的对象可以更快释放。
  • deallocating:标志对象是否正在释放内存。
  • has_sidetable_rc:当对象引⽤技术⼤于 10 时,则需要借⽤该变量存储进位。
  • extra_rc:当表示该对象的引⽤计数值,实际上是引⽤计数值减 1,例如,如果对象的引⽤计数为 10,那么 extra_rc 为 9。如果引⽤计数⼤于 10,则需要使⽤到上⾯的 has_sidetable_rc。
    image.png

位域与联合体

通过探索可以知道isa_t是一个联合体位域结构那么什么是粘合体位域?

  • 位域: 位域就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作——这样就可以把几个不同的对象用一个字节的二进制位域来表示。位域是C语言一种数据结构
  • 使用位域的好处是:
    • 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。这样节省存储空间,而且处理简便。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
    • 可以很方便的利用位域把一个变量给按位分解。比如只需要4个大小在0到3的随即数,就可以只rand()一次,然后每个位域取2个二进制位即可,省时省空间。

位域的使用

C语言中,位域的生命和结构体类似不同是:

  • 在声明时,位域成员必须是整形或枚举类型(通常是无符号类型)
  • 成员名的后面是一个冒号和一个整数,整数规定了成员所占用的位数
  • 位域不能是静态类型。不能使用&对位域做取地址运算,因此不存在位域的指针,编译器通常不支持位域的引用(reference)

分别创建位域和结构体对比:

struct Stuct {
    // (数据类型 元素);
    int  a;// 4字节 0 1 2 3
    char b;// 1字节 4。不够8字节补齐
}Stuct1;

struct Uni{
    // (数据类型 位域名: 位域长度);
    int a : 1;
    long b : 1;
}Uni1;
来看下效果: image.png
image.png

联合体

  • 联合体结构体的区别: 结构体中每个成员都会独立一块内存,相互不影响!而联合体所有成员公用一块内存!牵一而动全身!
1.1 联合体特征
union PPP {
    int a;      //4个字节
    short b;    //2个字节
    char c;     //1个字节
} p;

NSLog(@"union size: %lu - %lu",sizeof(p), sizeof(union PPP));
结果:
union size: 4 - 4


union PPP {
    int a;      //4个字节
    short b;    //2个字节
    char c;     //1个字节
    long d;     //8个字节
} p;
结果
union size: 8 - 8

可见:联合体的内存是成员所需的最大内存那个。

1.2 联合体特征
 p.a = 2;
NSLog(@"%d---%d---%c---%d",p.a,p.b,p.c,p.d);//2---2---�---2
p.b = 4;
NSLog(@"%d---%d---%c---%d",p.a,p.b,p.c,p.d);//4---4---�---4
p.c = 'c';
NSLog(@"%d---%d---%c---%d",p.a,p.b,p.c,p.d);//99---99---c---99

可见:每次改变联合体中的成员,其他成员会受到影响、即联合体成员之间是相互互斥的

相关文章

  • iOS-isa结构探一探

    本文仅记录笔者的学习过程,只代表笔者个人的理解,如果有错的地方,欢迎各位指正! OC对象的本质 对象继承与NSOb...

  • 类的结构探一探

    对象、类、元类、根元类的关系 先温习下这个图先背背图 类的本质 首先准备可调试objc源码,配置可执行的objc源...

  • cache_t结构探一探

    接上文类的结构分析 一.cache_t结构 1.cache_t结构 cache是cache_t类型,那么cache...

  • iOS-类结构(中)

    类结构 hxdm,在iOS-isa指向图&类结构[https://www.jianshu.com/p/782ccf...

  • iOS-类结构(下)

    在iOS-isa指向图&类结构[https://www.jianshu.com/p/782ccfc77f1c]中提...

  • 一探深绿

    没有说走就走的自由,千亩栀子花地头,亦可去一探深绿。 18点日影斜长。山穿绿妆醉晚霞,水浸倒影疑南天。 栀子林下水...

  • 水库一探

    这水库冬季枯竭的,只剩下一点点水了。 高高的坝体上有一些太阳能装置。这个位置的光照一定最好,可惜今天是烟雨蒙蒙,看...

  • 内存对齐探上一探

    写在前面: 在了解内存对齐之前先了解一下各数据类型在内存中的大小,目前我们比较常用的是64位系统,所以我们的研究对...

  • 哪些因素可以定义自己,真的可以吗?

    如何定义自己? 坐在西湖畔,看着微波荡漾的湖水,依稀有游船点缀,一只鸭子潜入水下扎个猛子,继而浮出水面,一探一探脖...

  • brave

    不想无知 不想自私 有一探到底的勇气 无知的勇气

网友评论

      本文标题:iOS-isa结构探一探

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