NSString有三种类型管理方式:__NSCFConstantString类型,__NSCFString类NSTaggedPointerString类型。
NSString *firstString = @"helloworld";
NSString *secondString = [NSString stringWithFormat:@"helloworld"];
NSString *thirdString = @"hello";
NSString *fourthSting = [NSString stringWithFormat:@"hello"];
NSLog(@"%p %@\n%p %@\n%p %@\n%p %@\n",firstString,[firstString class],secondString,[secondString class],thirdString,[thirdString class],fourthSting,[fourthSting class]);
一、三直接赋值不管长短,类型相同,地址相邻,都为__NSCFConstantString类型
二为__NSCFString类型,四为NSTaggedPointerString类型
__NSCFConstantString
通俗理解其就是常量字符串,是一种编译时常量
这种对象存储在字符串常量区。
当我们使用不同的字符串对象进行创建时当内容相同,其对象的地址也相同,这也就证明了常量字符串是一种单例。
__NSCFString
和 __NSCFConstantString 不同, __NSCFString 对象是在运行时创建的一种 NSString 子类,他并不是一种字符串常量。所以和其他的对象一样在被创建时获得了 1 的引用计数。
这种对象被存储在堆上。
通过 NSString 的 stringWithFormat 等方法创建的 NSString 对象一般都是这种类型。
如果字符串长度大于9或者如果有中文或其他特殊符号(可能是非 ASCII 字符)存在的话则会直接成为 __NSCFString 类型
NSTaggedPointerString
理解这个类型,需要明白什么是标签指针,
这是苹果在 64 位环境下对 NSString,NSNumber 等对象做的一些优化。简单来讲可以理解为把指针指向的内容直接放在了指针变量的内存地址中,因为在 64 位环境下指针变量的大小达到了 8 字节足以容纳一些长度较小的内容。于是使用了标签指针这种方式来优化数据的存储方式。
从其的引用计数可以看出,这也是一个释放不掉的单例常量对象。当我们使用不同的字符串对象进行创建时当内容相同,其对象的地址也相同。在运行时根据实际情况创建。
对于 NSString 对象来讲,当非字面值常量的数字,英文字母字符串的长度小于等于 9 的时候会自动成为 NSTaggedPointerString 类型。
接下来我们看一下用stringWithformat赋值时什么时候为__NSCFString类型什么时候为NSTaggedPointerString类型。
NSString *a = [NSString stringWithFormat:@"a"];
NSString *aa = [NSString stringWithFormat:@"a"];
NSString *b = [NSString stringWithFormat:@"牛"];
NSString *bb = [NSString stringWithFormat:@"牛"];
NSLog(@"%@ %@",[a class], [b class]);
NSLog(@"%p %p %p %p", a, aa, b, bb);
NSLog(@"%d %d", (a ==aa), (b == bb);
我们可以看出当字符串为中文时,其类型为NSCFStrig,对象存储在堆区。
当字符串为英文字母且长度小于9,其类型为NSTaggedPointString 。其对象是一个释放不掉的单例常量对象,对象存储在字符串常量(把指针指向的内容直接放在了指针变量的内存地址中,因为在 64 位环境下指针变量的大小达到了 8 字节足以容纳一些长度较小的内容。于是使用了标签指针这种方式来优化数据的存储方式)
所以a与aa的对象地址相同,b与bb的地址不同。