一概述
本文主要是关于基于64位系统下,NSString对象的retainCount分析。
二内容
1.以下对象的retainCount为18446744073709551615也就是2的64次方减一,也就是ULONG_MAX
// @"123456789"是一个字符串常量,数据保存在数据区,不在堆区,是不可能
// 被释放的,所以他的retainCount为ULONG_MAX,就算执行release也不会有变化
// 这三个的内存地址也是一样的0x10bc89060,他们的class类型都为__NSCFConstantString
NSString *name = @"123456789";
NSString * name = [NSString stringWithString:@"123456789"];
NSString * name = [[NSString alloc] initWithString:@"123456789"];
// 下面的内存地址于上面的不一样,为0xa1ea1f72bb30ab19,class类型为NSTaggedPointerString
NSString * name = [NSString stringWithFormat:@"123456789"];
NSTaggedPointerString这个类表示这是字符串的一种指针Tagged Pointer,在苹果推出了 采用64位架构的A7双核处理器 iphone 5s的时候,为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念,这是为了从32位机器迁移到64位机器中后节省内存。在苹果的64位OC实现中,若对象指针的二进制第一位是1,则该指针为Tagged Pointer。
例如0xa1ea1f72bb30ab19其中a的2进制为1010,第一位1表示这是Tagged Pointer,010表示这是一个NSTaggedPointerString类;这个地址最后一位表示字符串的数目,这里是0101表示有9位字符串;其中真正用来存储的位数只有中间的14位16进制。这个地址本身其实就存储了字符串的值,可以说是存储在&strS内存中值,只是伪装成了地址,它不需要存储在数据区,也不需要申请堆空间。
NSTaggedPointerString的存储有三种编码方式:ASCII码,六位编码,五位编码。
六位编码:
NSTaggedPointerString 采用六位二进制编码,(14*4)/6=9.333…,可以看出最多存储9位字符。字符数目8~9
五位编码:
采用五位二进制编码,(14*4)/5 = 11.2,可以看出这种编码最多存储11位字符。字符数目在10~11
1.以下对象的retainCount为1,class类型都为__NSCFString
// @"hello boy %@"和@"jack"这两个是字符串常量,是无法修改的,所以由这两个组成的是一个新的字符串就不能再他们基础上修改得到,只能申请新的内存,只能在堆里面申请,所以这时产生的NSString对象的retainCount为1
NSString *name = [[NSString alloc] initWithFormat:@"hello boy %@",@"jack"];
// 比较之前使用的stringWithFormat参数的对象的retainCount于之不一样,因为现在的字符串超过了9个,所以无法使用NSTaggedPointerString来保存,只能申请新的内存,所以这时产生的NSString对象的retainCount为1
NSString * name = [NSString stringWithFormat:@"1234567890"];
备注:因为retainCount返回的数据类型为NSUInteger,在64为系统中为8个字节无符号整数,打印的时候需要使用%lu,如果使用%d,则本应该输出为2^64-1,会变成-1。
这是因为,数据保存的方式是补码。正整数的原码、反码、补码完全一样,即符号位固定为0,数值位相同;负整数的符号位固定为1,由原码变为补码时,规则如下: 1原码符号位1不变,整数的每一位二进制数位求反,得到反码;2反码符号位1不变,反码数值位最低位加1,得到补码。无符号2^64-1保存的二进制格式为64个1,如果按照有符号打印,按照补码反推,1反码符号位1不变,反码数值位最低位减一1得到63个1加上最后一个0,2原码符号位1不变,整数的每一位二进制数位求反得到开头为1中间为62个0末尾为1的二进制数,由于打印是%d,所以取32个低位数,得到31个0和末尾一个1,刚好是-1.