美文网首页
类的内存布局之isa指针

类的内存布局之isa指针

作者: 答案不止一个 | 来源:发表于2020-09-08 23:09 被阅读0次

[Cls alloc] 在计算完大小,并且分配空间之后,就去执行initInstanceIsa。现在继续跟着源码看下initInstanceIsa 是起到什么作用,做了那些操作

isa_t 的定义

是一个共用体,成员包括cls,bits,和一个结构体数据。公用一个内存,同时只会代表一个数据。
使用不同的位域表示不同的属性比如bool类型的属性,可以节省大量的存储空间

union 共用体/联合体
  1. 共用体的所有成员占用同一段内存,修改一个成员,也会影响其他成员的数据。可以看作同时只有一个成员是有准确数据的。
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {            //  是一个结构体位域成员,结构题的具体成员在ISA_BITFIELD中定义
        ISA_BITFIELD;  // defined in isa.h 
    };
#endif
};
// ISA_BITFIELD  真机arm64 下的定义
#   define ISA_BITFIELD  
// : 1; 数字代表这个占据了多少个0/1位。
uintptr_t nonpointer        : 1;// 代表普通的指针,存储着Class、Meta-Class对象的内存地址。1,代表优化过,使用位域存储更多的信息
uintptr_t has_assoc         : 1; //是否存在关联对象,如果没有,释放时会更快
uintptr_t has_cxx_dtor      : 1; //是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快。有的话需要做析构操作。
uintptr_t shiftcls          : 33; // 存储着类信息。cls >> 3 对象是否完成初始化 存储着Class、Meta-Class对象的内存地址信息
/*MACH_VM_MAX_ADDRESS 0x1000000000*/
uintptr_t magic             : 6; // 用于在调试时分辨对象是真的对象还是没有初始化的空间
uintptr_t weakly_referenced : 1; // 是否有被弱引用指向过,如果没有,释放时会更快
uintptr_t deallocating      : 1; // 对象是否正在释放
uintptr_t has_sidetable_rc  : 1; // 当对象引⽤技术⼤于 10 时,则需要借⽤该变量存储进位
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
uintptr_t extra_rc          : 19 // 当表示该对象的引⽤计数值,实际上是引⽤计数值减 1,
例如,如果对象的引⽤计数为 10,那么 extra_rc 为 9。如果引⽤计数⼤于 10,
则需要使⽤到下⾯的 has_sidetable_rc。
isa64情况.jpeg
结构体位域

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。比如一个Bool类型,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。

所谓“位域”是把一个字节中的二进位划分为几 个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。

alloc中调用:initInstanceIsa -> initIsa

inline void  objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
    // 
    initIsa(cls, true, hasCxxDtor);
}

inline void  objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    // 根据上面initIsa(cls, true, hasCxxDtor)调用; 传入的是true。进入else
    if (!nonpointer) {
        isa = isa_t((uintptr_t)cls);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());

        // 一个新的 isa_t的结构体。
        isa_t newisa(0);

// #if __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && !__LP64__)
// #   define SUPPORT_INDEXED_ISA 1
// #else
// #   define SUPPORT_INDEXED_ISA 0
// #endif
//  SUPPORT_INDEXED_ISA 在arm64 下可以看作false
#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; // 包含了 magic,nonpointer 信息 
        // 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;//shiftcls 类指针的非零位 Class pointer's non-zero bits.

#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;
    }
}
  1. 创建新的isa_t 联合体
  2. 使用 ISA_MAGIC_VALUE初始化赋值magic 以及nonpointer 为1
  3. 复制has_cxx_dtor
  4. 将cls的地址右偏移3位(8的倍数,类的地址也是8的倍数,抹去后面的3个0,省空间。另外使用掩码获取cls时也不需要再移位)赋值给shiftcls。
ISA_MAGIC_VALUE 对应的数据

可以结合isa_t 的定义 看下各属性对应的值

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL // shiftcls 的掩码。isa & ISA_MASK 可以获得  shiftcls
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
WX20200908-224050@2x.png

位运算验证isa数据

调试过程中newisa 赋值前后的数据变化

// arm64  ISA_MAGIC_VALUE 0x000001a000000001ULL
// 
// p newisa
(isa_t) $8 = {
  cls = nil
  bits = 0
   = {
    nonpointer = 0
    has_assoc = 0
    has_cxx_dtor = 0
    shiftcls = 0
    magic = 0
    weakly_referenced = 0
    deallocating = 0
    has_sidetable_rc = 0
    extra_rc = 0
  }
}
// newisa 赋值之后
// p newisa
(isa_t) $17 = {
  cls = LGPerson
  bits = 8303516107940541
   = {
    nonpointer = 1 //代表优化过
    has_assoc = 0
    has_cxx_dtor = 1 //有C++的析构函数
    shiftcls = 536872023 // 类地址向右移动3位
    magic = 59 //
    weakly_referenced = 0
    deallocating = 0
    has_sidetable_rc = 0
    extra_rc = 0
  }
}
// newisa 对应的二进制数据   0b0000000000011101100000000000000100000000000000000010001010111101
// 可以看出执行的效果是将  nonpointer,has_cxx_dtor,shiftcls,bits进行了赋值

inline Class objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK); // isa & ISA_MASK 即可获取shiftcls << 3 。即 cls的地址
#endif
}

验证 isa的数据

在上面学习到对象的isa数据中存储着起类的地址。那么去查看类的存储空间中,存储哪些数据

@interface LGPerson : NSObject {
    int a_a;
    char b_b;
    NSString * c_c;
}
@property (strong, nonatomic) NSString * aaaaaa;
@property (assign, nonatomic) int bbbbb;
@property (assign, nonatomic) int ccccc;
@property (strong, atomic) NSString * ddddd;
@property (copy, nonatomic)NSString * eeeee;
@end

NSObject * p = [LGPerson alloc];

(lldb) x/2xg p
0x1006499e0: 0x001d8001000022bd 0x0000000000000000
(lldb) p/x 0x001d8001000022bd & 0x0000000ffffffff8 // 0x0000000ffffffff8 是ISA_MASK isa的掩码直接获取只想cls地址
(long) $2 = 0x00000001000022b8
(lldb) p/x LGPerson.class  // 看下LGPerson类信息的存储地址)I
(Class) $1 = 0x00000001000022b8 LGPerson // 和实例对象isa中地址相同

(lldb) x/2xg 0x00000001000022b8  // 现在LGPerson 的内部存储的数据 isa的指针 0x0000000100002290
0x1000022b8: 0x0000000100002290 0x0000000100333140 // 0x0000000100002290 是isa的数据;0x0000000100333140 是superclass NSObject
(lldb) p/x 0x0000000100002290 & 0x0000000ffffffff8 // 获取isa中的cls指针 
(long) $2 = 0x0000000100002290  // 说明类的isa指针就是一个单纯的cls指针

(lldb) p/x objc_getMetaClass("LGPerson")  // 看下LGPerson的mata的ckass
(Class) $4 = 0x0000000100002290 // 类中的isa指针指向的是LGPerson的mataClass

(lldb) x/4xg 0x0000000100002290   // 看下mataClass的isa指针
0x100002290: 0x00000001003330f0 0x00000001003330f0 // 
0x1000022a0: 0x00000001018070c0 0x0001e03500000007

(lldb) po 0x00000001003330f0  //可以得知mataClass的isa指针应该和NSObject有关系
NSObject

(lldb) x/4xg 0x00000001003330f0  // 然后后续只想的都是相同的地址 0x00000001003330f0
0x1003330f0: 0x00000001003330f0 0x0000000100333140
0x100333100: 0x000000010194bc40 0x0005e03100000007

(lldb) p/x NSObject.class
(Class) $15 = 0x0000000100333140 // NSObject // NSObject 的地址是 0x0000000100333140
(lldb) x/4xg 0x0000000100333140  // 查看NSObject 的isa指向 0x00000001003330f0
0x100333140: 0x00000001003330f0 0x0000000000000000
0x100333150: 0x000000010064a370 0x0001801000000003

(lldb) p/x objc_getMetaClass("NSObject") // 可以知道 0x00000001003330f0 是NSObject的元类地址
(Class) $9 = 0x00000001003330f0

isa流程图.png
i386是针对intel通用微处理器32位处理器
x86_64是针对x86架构的64位处理器

模拟器32位处理器测试需要i386架构,
模拟器64位处理器测试需要x86_64架构,
真机32位处理器需要armv7,或者armv7s架构,
真机64位处理器需要arm64架构

相关文章

  • 类的内存布局之isa指针

    [Cls alloc] 在计算完大小,并且分配空间之后,就去执行initInstanceIsa。现在继续跟着源码看...

  • 4-1 内存管理

    1.内存布局 2.iOS内存管理方案 [isa指针保存了内存管理的信息] 2.iOS内存管理方案 [isa指针...

  • Runtime(8)--isa指针

    isa指针 对象的isa指针,用来表明对象所属的类的类型。但是如果isa指针仅表示类型的话,对内存显然也是一个极大...

  • iOS底层之类的结构分析

    从iOS底层之isa结构分析及关联类我们探究了类的实例对象的内存结构,对象指针的首地址存储了isa,也就是存储了类...

  • 1.8、iOS面试题之语法

    1、isa指针?(对象的isa,类对象的isa,元类的isa都要说) 对象的isa指针指向所属的类 类的isa指针...

  • Objective-C实例对象的内存布局

    结论: 实例对象的内存布局情况如下:ISA指针,根类的实例变量,倒数第二层父类的实例变量,…,父类的实例变量,类的...

  • iOS开发中方法查找流程图

    实例对象的isa指针指向类对象,类对象的isa指针指向元类,元类的isa指针指向根元类,根元类的isa指针指向自己...

  • iOS class(类)对象

    每个类在内存中有且只有一个class对象class对象在内存中存储的信息包括isa指针superclass指针类的...

  • 随笔记录(一)、isa 和 superClass

    isa 指针 instance(实例对象) isa 指针,指向的是==类对象==。 class(类对象) isa ...

  • isa指针superclass

    isa指针的指向 实例对象的isa指针指向类对象 类对象的isa指针指向元类对象 isa的作用就是用来给实例找到各...

网友评论

      本文标题:类的内存布局之isa指针

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