OC

内存管理-Tagged Pointer

Posted on 2022-01-06,3 min read

从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储


概述

以NSNumber为例,在没有使用Tagged Pointer之前,NSNumber等对象需要动态分配内存、维护引用技术等,NSNumber指针存储的是堆中的NSNumber对象的地址值

在使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中

当指针不够存储数据时,才会使用动态分配内存的方式来存储数据

objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调佣开销

  • 以下述为例,第二个for循环,会在执行的时候crash!

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 1000; i++) {
        dispatch_async(queue, ^{
            self.testString =  [NSString stringWithFormat:@"abc"];
        });
    }
    
    for (int i = 0; i < 1000; i++) {
        dispatch_async(queue, ^{
            self.testString =  [NSString stringWithFormat:@"abcdefghijk"];
        });
    }
    
  • 分析:
    ARC的实现原理其实就是MRC,在MRC中,代码类似如下,由于上面的例子中,会异步调用 [_testString release] ,因此会导致提前release导致崩溃;

    // 模拟MRC
    - (void)setTestString:(NSString *)testString{
        if (_testString == testString) {
            [_testString release]; // 赋值为新对象前,先释放旧对象
            _testString = [testString copy];
        }
    }
    
    - (NSString *)testString{
        return _testString;
    }
    
    - (void)dealloc{
        [_testString release];
        _testString = nil;
        [super deallco];
    }
    
  • 那么为什么 if (_testString == testString) 没有拦截住呢?
    是因为每次 [NSString stringWithFormat:@"abcdefghijk"] 时分配的地址都不一样!

  • 那么为什么 self.testString = [NSString stringWithFormat:@"abc"] 没有问题呢?
    这就是 Tagged Pointer 的原因了!这里RunTime 判断到 Tagged Pointer 后直接进行赋值,而根本没有调用 set 方法(强调一下这里每次 [NSString stringWithFormat:@"abc"] 的值是一样的)

如何判断是否为 Tagged Pointer

翻阅RunTime源码,可以查找到 _objc_isTaggedPointer 方法,我们以OC的方式模拟实现如下:

// 如果是iOS平台 (指针的最 '高' 有效位是1,就是Tagged Pointer))
# define _OBJC_TAG_MASK (1UL<<63)

// 如果是Mac平台(指针的最 '低' 有效位是1,就是Tagged Pointer)
//# define _OBJC_TAG_MASK 1UL

BOOL isTaggedPointer(id pointer){
    return ((uintptr_t)pointer & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

- (void)taggerPointerTest{
    NSString *str = [NSString stringWithFormat:@"abcdefghijk"];
    NSString *str1 = [NSString stringWithFormat:@"abc"];
    NSLog(@"\nstr - %p: isTaggedPointer: %d\nstr1 -  %p isTaggedPointer: %d",str,isTaggedPointer(str),str1,isTaggedPointer(str1));
}
str - 0x600002660500: isTaggedPointer: 0
str1 -  0x81a8f3dc8fa2ffae isTaggedPointer: 1

下一篇: 内存管理-内存布局→

loading...