OC中对象的本质

2021-06-15  本文已影响0人  Eli_app

对象在C++层面的表现

我们先准备以下代码

@interface ELPerson : NSObject



@property (nonatomic, strong) NSString *ELname;



- (void)testfunction;



@end

用如下代码把他编译成c++代码

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

打开生成的.cpp文件,我们搜索一下,找到

struct ELPerson_IMPL {

    struct NSObject_IMPL NSObject_IVARS;

    NSString *_ELname;

};

我们继续搜索ELPerson_IMPL

struct NSObject_IMPL {

    Class isa;

};

他在c++层面就是这样一个结构体,里面并没有存储我们定义的方法。就是结构体指针isa

NSObjectruntime中对应的源码为

/// Represents an instance of a class.

struct objc_object {

  Class _Nonnull isa OBJC_ISA_AVAILABILITY;

};

类对象的源码应该为

struct objc_class : objc_object {

  // Class ISA;

  Class superclass;

  cache_t cache;       // formerly cache pointer and vtable

  class_data_bits_t bits;  // class_rw_t * plus custom rr/alloc flags

。。。函数

}

objc_class继承objc_object,也就继承了isa。这里也说明:对象包含类对象和实例对象。

探寻ISA的实现

我们继续探究ISA,他的源码应该为如下:(x86_64架构精简代码说明)

union isa_t {

。。。//省略

  struct {
   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 unused      : 1;                     \

   uintptr_t has_sidetable_rc : 1;                     \

   uintptr_t extra_rc     : 8

  }

};

nonpointer在0位,表示是否对isa指针开启指针优化。0:纯isa指针,这里面直接存储ClassMeta-Class 对象的地址(没有使用位域)1:代表优化过的,使用了位域,里面包含位域里面的这些信息(类对象地址,isa包含了类信息、对象的引用计数等),想要得到类对象元类对象,需要&上ISA_MASK(也可以使用地址平移)才能得到。

has_assoc在1位,表示关联对象标志位,0:没有,1:有。

has_cxx_dtor在2位,表示该对象是否有C++或者Objc的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象。

shiftclsx86架构中占用3~46位,表示存储类指针的值。开启指针优化的情况下,在arm64架构中占用3~35位。

magicx86架构中占用47~52位,在arm64架构中占用36~41位,用于调式器判断当前对象是真的对象还是没有初始化的空间。

weakly_referencedx86架构中占用第53位,在arm64架构中占用第42位,标志对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放。

unusedx86架构中占用第54位,在arm64架构中占用第43位,标志对象是否正在释放内存。

has_sidetable_rcx86架构中占用第55位,在arm64架构中占用第44位,表示当对象引用计数大于10时,则需要借用该变量存储进位。

extra_rcx86架构中占用56~63位,在arm64架构中占用45~63位,当表示该对象的引用计数值时,实际上是引用计数值减1,例如:如果对象的引用计数为10,那么extra_rc9,如果引用计数大于10,则需要使用到has_sidetable_rc

我们直接测试一下shiftcls位置是否是类的信息

(lldb)x/4gx per //打印对象地址,首地址为isa

0x10065c0d0: 0x011d8001000082e5 0x0000000000000000

0x10065c0e0: 0x0000000000000000 0x0000000000000000

(lldb) p/x per.class //打印类对象地址

(Class) $8 = 0x00000001000082e0 ELPerson

(lldb)p/x 0x011d8001000082e5 >> 3 << 20 >> 17 //算法在下面说明

(long) $9 = 0x00000001000082e0 //说明对象的isa里面包含类对象的地址信息

(lldb) 

位运算图解:

image

再打印一下二进制的内存情况,方便对照,如下:

(lldb) x/1gt per0x10065c0d0: 0b0000000100011101100000000000000100000000000000001000001011100101(lldb)
上一篇 下一篇

猜你喜欢

热点阅读