OC对象探究03:isa 解析

2020-10-21  本文已影响0人  开发狗

提前准备

为了探究对象在底层的实现方式,此次使用clang来进行探究。相关语句clang -rewrite-objc main.m -o main.cpp ,在终端中进入工程目录main.m所在的路径中,执行上面的语句,就会生成一个main.cpp的C++文件,该文件就是main.m文件的底层实现方式。

main.cpp探究

通过clang之后的main.cpp 文件可以看到如下代码:

struct YKPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_name;
};

可以看出我们创建的YKPerson类在底部被编译成了YKPerson_IMPL的结构体,name属性成为结构体的属性。所以我们可以得到第一个结论,对象在底层被编译成了结构体,对象在底层是以结构体的形式存在,而NSObject_ IVARS是在内部默认全部都有的属性isa

联合体位域

在日常开发中少不了在类中声明一些布尔或其他类型的属性用于判断。为了更高的利用内存,我们可以合理使用联合体位域的方式来减少对内存的占用。

@property (nonatomic, assign) BOOL front;
@property (nonatomic, assign) BOOL back;
@property (nonatomic, assign) BOOL left;
@property (nonatomic, assign) BOOL right;
union {
    char bits;
    struct {
          char front : 1;
          char back  : 2;
          char left  : 3;
          char right : 4;
    }
} _direction;

例如以上代码,日常开发中一般会使用第一段代码声明(front,back,left,right)四个属性来做相应的方向判断,并且在相应的setter,getter方法中进行相应的操作,因为BOOL类型是4字节类型,所以它们需要占用4*4 = 16字节 = 128位,只是为了做一个方向判断时,这个内存占用量其实也是不少的。所以这个时候我们可以用到联合体位域
在第二段代码中,因为char类型的数据为1字节,所以总共占用了5字节。每一个属性后面的数字代表的是所在的位置(例如:0000 1111,第一位代表front,第二位代表back,第三位代表3,第四位代表right)。使用联合体位域时需要配合相应的方法来对联合体位域中的属性进行相应设置。

isa分析

之前我们已经分析了对象在alloc时调用instanceSize计算内存空间,calloc像系统申请内存,接下来就是讲开辟的内存与对象类进行关联。通过源码分析,调用initInstanceIsa后在方法initIsa中进行处理,因此initIsa为核心方法。在该方法中主要是对isa的赋值操作,接下来对该方法做主要分析。属性isaisa_t类型,在该方法中对联合体isa_t isa中的相关属性进行了一些赋值操作。

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的定义,属性clsbits 互斥,只有当nonpointer不存在时cls才会被赋值,当nonpointer存在时,讲给bit赋值一个宏定义的ISA_INDEX_MAGIC_VALUE 0x001d800000000001ULL。重点在ISA_BITFIELD,以下是以x86架构下的关于相关定义,并对内部结构进行解读。

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)
LGPerson.png

在之前的文章中讲到对象调用alloc时,在_class_createInstanceFromZone 中与类进行关联,通过调试obj为返回结果,0x001d8001000020f1isa,我们将isa 进行位移计算,目的是得到isa_t 中的shiftcls,再以16进制打印传入的cls时我们发现此时的shiftcls就是我们的类,代表此时我们自定义的类已经被系统关联,并初始化了内存空间。

拓展 (clang)

结论

从以上可以得到在调用initIsa时是将系统为类创建的内存以及设置一些类的信息并与当前类进行关联。所以在isa 中保存了类的所有信息。

上一篇下一篇

猜你喜欢

热点阅读