iOS--内存管理 Tagged Pointer
Tagged Pointer
从64位开始iOS引入了Tagged Pointer技术,用于优化NSNumber NSDate NSString等小对象的存储,节省内存空间
在没有引入该技术之前,NSNumber等对象要分配内存,维护引用计数等,NSNumber指针存储的是堆中的NSNumber对象的地址,需要一个栈和一个堆的空间 开销很大,但是我只是存储一个整形用不着这么多空间。
使用tagged pointer之后,NSNumber指针存书的数据就变成Tag+Data,也就是说是将数据直接存储在指针中。
当NSNumber 存储很大的数据,动态分配内存,存在堆中。
最低有效位为1,那就是TaggedPointer类型,最低有效位为0,那就是对象类型。
NSNumber *number = @4;
[number intValue] ;
在objc_msgSend()中会判断是否是TaggedPoint 如果是那就直接从指针获取到对应的值。少耗性能。
在使用taggedPointer的时候需要注意点
- (void)test1{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for(int i =0; i <1000;i++){
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abcasasfdssafa"];
});
}
}
self.name = [NSString stringWithFormat:@"abcasasfdssafa"];会调用一个setter方法,改写成MRC格式
- (void)setName:(NSString *)name{
if(_name != name){
[_name release];
_name = [name retain];
}
}
有多条线程同时进入setter方法,那么就会存在多次释放_name 的操作,坏内存访问。
将改成atomic,在 dispatch_async 增加锁
- (void)test2{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for(int i =0; i <1000;i++){
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abc"];
});
}
}
赋值abc 直接存在Tagged Pointer指针中,不用走对象set方法
NSString *str1 = [NSString stringWithFormat:@"abcasasfdssafa"];
NSLog(@" before self.name===%p", str1);
NSString *str2 = [NSString stringWithFormat:@"abc"];
NSLog(@" after self.name===%p", str2);
before self.name===0x60000025e740 heap
after self.name===0xcd26d822a5963603 tagged pointer
NSString *str1 = [NSString stringWithFormat:@"abcasasfdssafa"];
NSLog(@" before self.name===%@", [str1 class]);
NSString *str2 = [NSString stringWithFormat:@"abc"];
NSLog(@" after self.name===%@", [str2 class]);
before self.name===__NSCFString
2020-10-25 12:15:15.910546+0800 LearnLoadInitial[23497:1909251] after self.name===NSTaggedPointerString
源码中objc-internal文件判断是否是Tagged Pointer
_objc_isTaggedPointer(const void * _Nullable ptr)
{
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
iOS平台
# define _OBJC_TAG_MASK (1UL<<63)
MACo平台
define _OBJC_TAG_MASK 1UL