iOS八股文(二)对象的本质探索(下)

718 阅读3分钟

属性存储位置重排

系统在存储属性自动生成的成员变量的时候,会对这些成员变量重新排列顺序,而不是我们编码的顺序,所有我们编码的时候可以不考虑属性顺序对内存的影响。但如果是成员变量则不会重新排列。

@interface OSTestObject : NSObject
@property (nonatomic, strong) NSObject *name;
@property (nonatomic, strong) NSObject *name2;
@property (nonatomic, assign) int count;
@property (nonatomic, copy) NSString *str;
@property (nonatomic, assign) NSInteger lcount;
@end

image.png

可以看到实际的结构是

 {
     int count,
     NSObject *name,
     NSObject *name2,
     NSString *str,
     NSInteger lcoun,
 }

其实重排顺序是为了优化内存。将零碎的存储空间整合,提高读取效率。

联合体

在C++语法中有除了结构体之外,还有一个联合体(union)。为了给下一节知识铺路,这一小节认识下联合体。

在进行某些算法的C语言编程的时候,需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术,几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构,在C语言中,被称作“共用体”类型结构,简称共用体,也叫联合体。

联合体和结构体一样也是一种数据结构。两者不同之处主要有以下2点:

  1. 结构体成员变量各自有各自的存储位置,联合体所有成员变量公用一块存储位置。
  2. 结构体的大小为所有成员变量大小的和(完事内存需要对齐)。联合体的大小是其中最大成员变量的大小。

在objc4源码中关于isa_t的定义就是一个联合体。

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    uintptr_t bits;
 }

IAS指针

上一节大致介绍了联合体,我们知道isa的本质其实是联合体。但具体有哪些信息呢?

在objc4源码中的isa_t定义中有一个结构体。

image.png 这个结构体用宏定义的方式表示,具体定义是根据结构模式和真机模拟器类型划分,详情见 isa.h文件,本文以arm64并且是真机模式为例讲解。

image.png

接下来详细介绍这几个成员变量的含义

  • nonpointer 是否是优化过的isa指针 苹果之前所有的结构都是32位的,在32位的时候isa指针只有指向对应类对象/元类对象的功能。而到了64位后,isa指针里面的信息更多,为了适配兼容,这一位用作区分。

  • has_assoc 是否存在关联对象 记录对象是否动态添加(使用runtime API),关联对象。如果有则为1,否则为0。在对向释放的时候要检 查这个信息。

  • has_cxx_dtor是否存在c++析构函数 一般有属性的时候就会有析构函数,析构函数是用来释放成员变量的。

  • shiftcls class对象或者meta-class对象地址 存放class对象或者meta-class对象地址,在消息发送(objc_msgSend)的时候去找对应方法时候使用。

  • magic 调试信息 用于在调试时分辨对象是否未完成初始化

  • weakly_referenced 是否有被弱指针指向 如果有被弱指针指向,会在释放的时候去操作。如果没有则释放得更快

  • unused 是否使用过

  • has_sidetable_rc 引用计数是否存在sidetable中

  • extra_rc 引用计数 如果引用计数大于这19位(arm64 & 真机)的最大值(2的19次方-1),则把has_sidetable_rc置位1,把引用计数去存到sidetable中。

当我们知道这些内容后,就能动态的去获取对象的引用计数了。

struct os_object {
    unsigned int _isa1;
    unsigned int _isa2;
};

@implementation NSObject (OSRetainCount)
- (int)selfRetainCount {
    // arm64 模拟器
    struct os_object *obj = (__bridge struct os_object *)self;
    unsigned int isa1= obj->_isa2;
    unsigned int count = isa1 >> 24;
    return count;
}
- (void)test1 {
    //引用计数位1
    NSObject *obj = [[NSObject alloc] init];
    NSLog(@"%d",[obj selfRetainCount]);

//    再加上255
    NSMutableArray *temp = [NSMutableArray array];
    for (int i = 1 ; i < 255; i++) {
        [temp addObject:obj];
        NSLog(@"%d",[obj selfRetainCount]);
    }
    NSLog(@"%@",temp);
}

打印结果:

image.png