iOS 中的Tagged Pointer

什么是Tagged Pointer对象

Tagged Pointer对象一般用于NSNumber、NSDate、NSString等小对象的存储。通常来说,普通对象对象需要动态分配内存、维护引用计数等,对象指针存储的是堆中的对象的地址值。而Tagged Pointer对象呢,其指针里面不是地址,而是它的值。所以Tagged Pointer实际上已经不能算是对象了,只是一个对象皮的普通变量。它的内存并不存在堆中,也不需要malloc和free。Tagged Pointer对象不仅节省内存,在内存读取和对象创建上效率大大提高。

什么样的对象算小对象?所有的NSNumber、NSDate、NSString都是小对象吗?

小对象往往指的是占内存较小的对象,小道什么程度呢?小到它的值可以存储在对象的指针里面。在iOS中对象的指针是8位,8*8=64bit,由于对象指针本身还要存储地址,对很多占用内存比较小的对象,比如NSNumber、NSDate、NSString,它们有时候内存很小,可以直接和地址存储在对象指针里面,不需要开辟堆空间来存储。但是当它们的内存比较大时,指针存不下时,也会开辟堆空间来存储。下面我们以字符串为例子来演示一下:
首先创建一个字符串属性,用strong修饰:

@property (nonatomic, strong) NSString *aString;

接着多线程给aString进行赋值操作,如demo1:

 dispatch_queue_t queue = dispatch_queue_create("com.djx.cn", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i<10000; i++) {
        dispatch_async(queue, ^{
            self.aString = [NSString stringWithFormat:@"123456789"];  
             NSLog(@"%@",self.aString);
        });
    }

这段代码运行时正常的。但是如果我们把字符串长度在增加一下,如demo2:

dispatch_queue_t queue = dispatch_queue_create("com.djx.cn", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i<10000; i++) {
        dispatch_async(queue, ^{
            self.aString = [NSString stringWithFormat:@"1234567890"];  
             NSLog(@"%@",self.aString);
        });
    }

这段代码就会崩溃,为什么?这里demo2和demo1的不同仅仅是因为demo1的字符串长度为9位,而demo2字符串长度为10位,就多了以为,就会崩溃。补充:字符串的在内存中比较特殊,字符串主要有三种存储形式:

1、Tagged Pointer对象,就像上面介绍的那样;
2、存储在常量区,比如aString = @"ureryueyr"等,这种在编译期就能确定的就存在常量区,不存在引用计数管理的问题;
3、普通的字符串对象,通过stringWithFormat创建较长的字符串,比如上面的demo2,此时跟其他OC对象没有区别。

分析:首先因为属性aString是用strong修饰的,demo2崩溃的原因是因为set方法里进行了retain和release操作,在release操作的时候,由于多线程的原因,有可能变量刚release,又被其他线程release导致过度释放的问题。但是demo1为什么就不会崩溃的?原因在于demo1中的对象是个Tagged Pointer对象。怎么知道?
demo1运行时:

tagged_point.jpeg

demo2运行时:
普通对象.jpeg

对比demo1和demo2发现,demo1的对象是Tagged Pointer对象,demo2是普通的对象。为什么Tagged Pointer对象多线程set就不会崩溃呢?

Tagged Pointer对象的内存管理

它们内存管理是没有通过引用计数来管理,自然就会出现像普通对象的过度释放的崩溃信息:

__attribute__((aligned(16), flatten, noinline))
id 
objc_retain(id obj)
{
    if (obj->isTaggedPointerOrNil()) return obj;
    return obj->retain();
}

__attribute__((aligned(16), flatten, noinline))
void 
objc_release(id obj)
{
    if (obj->isTaggedPointerOrNil()) return;
    return obj->release();
}

__attribute__((aligned(16), flatten, noinline))
id
objc_autorelease(id obj)
{
    if (obj->isTaggedPointerOrNil()) return obj;
    return obj->autorelease();
}

通过源码可以看到,如果是Tagged Pointer对象,就不会进行retain和release操作。

你可能感兴趣的:(iOS 中的Tagged Pointer)