测试代码:
NSString *str1 = [NSString stringWithFormat:@"小强"];
NSString *str2 = [NSString stringWithFormat:@"1"];
NSString *str3 = [NSString stringWithFormat:@"a"];
NSString *str4 = @"123456789";
上面这段代码控制台显示为:
我们知道oc存储主要分成数据区、堆区和栈区,
__NSCFConstantString
显然是常量字符串,自然就是存储在常量区。__NSCFString
表示为oc对象,NSString
就是封装的CFString
,0x6000000315c0
地址显示这个字符串对象存储在堆中。NSTaggedPointerString
这个类表示这是字符串的一种指针Tagged Pointer,0xa636261646362617
这个地址为什么如此与众不同呢,接下来我们就简单介绍这钟字符串的存储指针。
在苹果的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类。