iOS-Tagged Pointer的理解

关于Tagged Pointer

在2013年9月,苹果推出了iPhone5s,与此同时,iPhone5s配备了首 个采用64位架构的A7双核处理器,为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念。先看看原有的对象为什么会浪费内存。假设要存储一个NSNumber对象,其值是一个整数。正常情况下,如果这个整数只是一个NSInteger的普通变量,那么它所占用的内存是与CPU的位数有关,在32位CPU下占4个字节,在64位CPU下是占8个字节的。而指针类型的大小通常也是与CPU位数相关,一个指针所占用的内存在32位CPU下为4个字节,在64位CPU下也是8个字节。所以一个普通的iOS程序,如果没有Tagged Pointer对象,从32位机器迁移到64位机器中后,虽然逻辑没有任何变化,但这种NSNumber、NSDate一类的对象所占用的内存会翻倍。

简述:

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

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

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

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

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

  • 如何判断一个指针是否为Tagged Pointer?

    iOS平台,最高有效位是1(第64bit)

    Mac平台,最低有效位是1

如何进行判断是否是Tagged Pointer?

 

Tagged Pointer 示例

首先先看NSNumber数值对象

muStr2 = [NSMutableString stringWithString:@"1"];
for(int i=0; i<20; i+=1){
    NSNumber *number = @([muStr2 longLongValue]);
    NSLog(@"%@, %p", [number class], number);
    [muStr2 appendString:@"1"];
}
// 输出结果
__NSCFNumber, 0xb000000000000013
__NSCFNumber, 0xb0000000000000b3
__NSCFNumber, 0xb0000000000006f3
__NSCFNumber, 0xb000000000004573
__NSCFNumber, 0xb00000000002b673
__NSCFNumber, 0xb0000000001b2073
__NSCFNumber, 0xb0000000010f4473
__NSCFNumber, 0xb00000000a98ac73
__NSCFNumber, 0xb000000069f6bc73
__NSCFNumber, 0xb000000423a35c73
__NSCFNumber, 0xb000002964619c73
__NSCFNumber, 0xb000019debd01c73
__NSCFNumber, 0xb000102b36211c73
__NSCFNumber, 0xb000a1b01d4b1c73
__NSCFNumber, 0xb00650e124ef1c73
__NSCFNumber, 0xb03f28cb71571c73
__NSCFNumber, 0xb27797f26d671c73
__NSCFNumber, 0x60000003d540
__NSCFNumber, 0x61000003cb40
__NSCFNumber, 0x61800003c760

数值是1、11、111、1111…..这样递增,可以从输出指针的地址看出最低4位一直为3,这个用于标记是long(float则为4,Int为2,double为5),而最高4位的“b”表示是NSNumber类型;其余56位则用来存储数值本身内容。当存储用的数值超过56位存储上限的时候,那么NSNumber才会用真正的64位内存地址存储数值,然后用指针指向该内存地址。(如果数值长度超过64位,那么就crash)。
因为Tagged Pointed不是一个真正的对象,所以其没有isa。不过只要避免在代码中直接访问对象的isa变量,就没问题。具体如Tagged Pointer 怎么访问类方法列表,之后再详细看下,也许是根据最够为的类型标记,然后调用对应的class方法列表。

再来看看Tagged Pointer String

NSString *str = @"A";
NSString *str2 = [[str mutableCopy] copy];
NSLog(@"str:%p %@", str, str.class);
NSLog(@"str2:%p %@", str2, str2.class);
// 输出结果
str:0x1068a2148 __NSCFConstantString
str2:0xa000000000000411 NSTaggedPointerString

String的TaggedPointer大致和Number一样,最高位表示类型,最低位表示字符串长度,然后字符串内容转为为ASCII码存储(上面的例子A的ASCII为65,转换为16进制是41,而1的ASCII码是49,转换为十六进制则是31)

NSMutableString *muStr2 = [NSMutableString stringWithString:@"1"];
for(int i=0; i<14; i+=1){       
    NSString *strFor = [[muStr2 mutableCopy] copy];
    NSLog(@"%@, %p", [strFor class], strFor);
    [muStr2 appendString:@"1"];
}
// 输出结果
NSTaggedPointerString, 0xa000000000000311
NSTaggedPointerString, 0xa000000000031312
NSTaggedPointerString, 0xa000000003131313
NSTaggedPointerString, 0xa000000313131314
NSTaggedPointerString, 0xa000031313131315
NSTaggedPointerString, 0xa003131313131316
NSTaggedPointerString, 0xa313131313131317
NSTaggedPointerString, 0xa0079e79e79e79e8
NSTaggedPointerString, 0xa1e79e79e79e79e9
NSTaggedPointerString, 0xa03def7bdef7bdea
NSTaggedPointerString, 0xa7bdef7bdef7bdeb
__NSCFString, 0x60000003e7c0
__NSCFString, 0x61800003e5a0
__NSCFString, 0x60800003e2c0

从上面的指针输出可以看出,最低位表示字符串的长度,而其余的56位也是用来存储数组,这里需要注意的是,当字符串内存长度超过了56位的时候,Tagged Pointer并没有立即用指针转向,而是用了一种算法编码,把字符串长度进行压缩存储(具体算法我还不太明白),当这个算法压缩的数据长度超过56位了才使用指针指向。

推荐两篇文章:

https://www.jianshu.com/p/e354f9137ba8

http://www.cocoachina.com/ios/20150918/13449.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值