objective-c对象缓存与tagged指针优化

背景

工程中出现了一段看似必崩的代码,比较好奇,代码如下:

– (void)setSomeThing:(BOOL)someThing {
objc_setAssociatedObject(self, kIsPreviewKey, [NSNumber numberWithBool:someThing], OBJC_ASSOCIATION_ASSIGN);
}

– (BOOL)isPreview {
id temp = objc_getAssociatedObject(self, kIsPreviewKey);
return [temp boolValue];
}

上述在一个类中,动态添加了一个 BOOL 类型的属性。以上是属性的 setter+getter 方法,已知以 OBJC_ASSOCIATION_ASSIGN 为参数是无法存储对象类型变量的,setter方法中 [NSNumber numberWithBool:someThing] 返回的变量并没有增加引用计数,离开 setter 方法后将在当前 runloop 末尾收到 release 消息后被置 nil,推知 getter 方法中 temp 指针实际指向的空间已经释放,于是 一个野指针异常条件满足。

然而,实际上这段代码非但不会崩溃,并且也实现了编写者预期的功能,能正确的存取动态添加的属性,结果令人吃鲸。

对象缓存机制

另一番测试:以 BOOL 类型变量初始化 NSNumber 对象,以 10 以下的整型变量初始化 NSNumber 对象。用例例如(在32位机器上运行):

– (void)viewDidLoad{
    [super viewDidLoad];
    NSNumber *numTempInt = [NSNumber numberWithInt:3];
    NSNumber *numTempBool = [NSNumber numberWithBool:YES];
    NSLog(@”temp_int_p_2%p”,numTempInt);
    NSLog(@”temp_bool_p_2%p”,numTempBool);
    [self testFunc];
}

– (void)testFunc{
    NSNumber *numTempInt = [NSNumber numberWithInt:3];
    NSNumber *numTempBool = [NSNumber numberWithBool:YES];
    NSLog(@”temp_int_p_1=%p”,numTempInt);
    NSLog(@”temp_bool_p_1%p”,numTempBool);
}

得到的结果 temp_int_p_1 与 temp_int_p_2 相同,temp_bool_p_1 与 temp_bool_p_2 相同。然而尝试100以上的较大数字,结果不再神奇,对应的值不在相同。

因为系统对 NSNumber 对象的缓存,部分常用的较小的值,初始化以后常驻内存,新初始化相同的值不再重新分配内存并初始化。

tagged 指针优化

简介如下:在 64 位 IOS 机器上,苹果为优化内存占用,优化运行速度等考虑,提出并实现了 tagged 指针机制。

tagged 指针特征表现如下:使用 numberWithXXX : argu 初始化对象时,若参数数据实际占用的空间小于64-8-1=55位, 即以整型数据为例,去掉一位符号位,最大值占 54 位,即 2^55-1,这样得到的 NSNumber 对象并不是真正的 OC 对象,其指针也并没有指向 OC 对象,输出指针地址易见指针中除去末位和首字节以外,其余各位组成的数字等于 argu 的值。

经测试,末位字节用来表示 argu 参数的类型。

使用上文中对象缓存机制的实例代码后,得到的结果和对象缓存实验结果相同,但temp_int_p_1与temp_int_p_2,以及 temp_bool_p_1与temp_bool_p_2 有更明显的特征(tagged 指针特征)。

对于较小的类型如 NSNumber NSDate 等,在64位机器上,使用 tagged 指针优化还是很常见的;而在 32 位机器上,对象缓存机制也做到了对小型对象的初始化和访问优化。

你可能感兴趣的:(objective-c对象缓存与tagged指针优化)