NSString存储管理--NSTaggedPointerString

    NSString *strS = @"123456789";
    NSLog(@"%@ : %p",strS.class,strS);

    strS = [NSString stringWithFormat:@"1"];
    NSLog(@"%@ : %p",strS.class,strS);
    strS = [NSString stringWithUTF8String:"1234567"];
    strS = [NSString stringWithUTF8String:"abcdabc"];//7
    NSLog(@"%@ : %p",strS.class,strS);

    strS = [NSString stringWithUTF8String:"abcdabcd"];//8
    strS = [NSString stringWithUTF8String:"teeeeeeet"];//9
    NSLog(@"%@ : %p",strS.class,strS);

    strS = [NSString stringWithUTF8String:"eeeeeeeeee"];//10
    strS = [NSString stringWithUTF8String:"eeeeeeeeeee"];//11
    NSLog(@"%@ : %p",strS.class,strS);

    strS = [NSString stringWithUTF8String:"eeeeeeeeeeee"];//12
    NSLog(@"%@ : %p",strS.class,strS);

上面这段代码输出为:

__NSCFConstantString : 0x10c843820
NSTaggedPointerString : 0xa000000000000311
NSTaggedPointerString : 0xa636261646362617
NSTaggedPointerString : 0xa040000000000049
NSTaggedPointerString : 0xa00000000000000b
__NSCFString : 0x6000000315c0

我们知道oc存储主要分成数据区、堆区和栈区,

  • __NSCFConstantString显然是常量字符串,地址0x10c843820自然就是存储在数据区。
  • __NSCFString表示为oc对象,NSString就是封装的CFString0x6000000315c0地址显示这个字符串对象存储在堆中。
  • NSTaggedPointerString这个类表示这是字符串的一种指针Tagged Pointer,0xa636261646362617这个地址为什么如此与众不同呢,接下来我们就简单介绍这钟字符串的存储指针。

在苹果推出了 采用64位架构的A7双核处理器 iphone 5s的时候,为了节省内存和提高执行效率,苹果提出了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一类的对象所占用的内存会翻倍。

感兴趣的朋友可以去尝试下,这里只列出了NSString采用这种技术的结果。

对于以前的@""符号创建的字符串还是常量字符串,这个没有改变,但是采用stringWithFormat 等NSString方法的创建字符串对象则有了区别。

在苹果的64位OC实现中,若对象指针的二进制第一位是1,则该指针为Tagged Pointer。
例如0xa000000000000311其中a的2进制为1010,第一位1表示这是Tagged Pointer,010表示这是一个NSTaggedPointerString类;这个地址最后一位表示字符串的数目,这里是0001表示有1位字符串;其中真正用来存储的位数只有中间的14位16进制。这个地址本身其实就存储了字符串的值,可以说是存储在&strS内存中值,只是伪装成了地址,它不需要存储在数据区,也不需要申请堆空间。

NSTaggedPointerString的存储有三种编码方式:ASCII码,六位编码,五位编码。

ASCII码

我们发现NSTaggedPointerString存储内容除去第一位和最后一位,其实只有中间的14位16进制字符,再看ascll码由8位二进制组成,所以这里(14*4) / 8=7,用8位的ascll码的话最多可以存储7个字符。字符串数目0~7之间

[NSString stringWithFormat:@"1"]输出的地址 0xa000000000000311,其中31的2进制是0011 0001,在ascll码表里查找发现正是对应着“1”;

[NSString stringWithUTF8String:"abcdabc"] 这里输出的地址是0xa636261646362617 可以发现在使用ascll编码时,字符串对应的编码是从右向左存储的

六位编码:

NSTaggedPointerString 采用六位二进制编码,(14*4)/6=9.333…,可以看出最多存储9位字符。字符数目8~9

五位编码:

采用五位二进制编码,(14*4)/5 = 11.2,可以看出这种编码最多存储11位字符。字符数目在10~11

NSTaggedPointerString 存储编码中的六位和五位编码都是根据通常代码中字母使用频率来排序的,但并不是一成不变的,apple会持续更新并统计字母使用频率,系统每次升级都可能不一样,当前第一位是字母e,之后是i,l,o,t…;这两种编码是从左向右的;根据编码位数我们显然也能推测出并不是所有字符都可以进行ascll或者六位五位编码的,当出现这样不能编码的时候,系统也就不会使用NSTaggedPointerString类。



参考:
1.Tagged Pointer Strings
2.iOS Tagged Pointer

你可能感兴趣的:(NSString存储管理--NSTaggedPointerString)