再探NSString

再探NSString

NSString应该是oc开发中最经常使用的一个数据类型了,此次对该类型再进行一次全方位的探索与总结。设计模式

NSString本质上属于OC类对象,继承于NSObject,遵照NSCopying, NSMutableCopying, NSSecureCoding协议。架构

NSMutableString与之相似,惟一不一样的是它继承于NSString。框架

经过语法糖建立NSString

大部分的开发中,咱们都会使用@+双引号的方式建立NSString对象。以下:设计

NSString* str=@"i am a single str";

这种建立方式建立出来的类型是什么呢?它存储的空间地址又在哪里呢?不妨打印一下:3d

NSLog(@"%@:%p",[str class],str);
//打印以下:
//__NSCFConstantString:0x100002058

为何明明是NSString类型的在这里会变成__NSCFConstantString类型,这是由于NSString实际上是个类族(大部分容器类也是如此),它的初始化方法返回的实例对象实际上是隐藏在类族中的公共接口后面的某一内部类型。指针

类族(class cluster):属于一种设计模式,用以隐藏“抽象基类”背后的实现细节。oc的系统框架中广泛使用此模式。code

其实__NSCFConstantString类型是一种字符串的常量类型,当切换成MRC模式下使用retainCount会发现它的引用计数会很是大,一般为:2^64-1,这样设置的缘由就是不管对其进行屡次release都可以保证对象不会被释放,因此能够直接将其看做为一个单例。对于单例对象,那么只要有相同的内容,他们的内存地址也就相同。以下所示:orm

NSString* str0=@"i am not a single str";
NSString* str1=@"i am not a single str";
NSLog(@"%@:%p",[str0 class],str0);
NSLog(@"%@:%p",[str1 class],str1);

//打印以下:
//__NSCFConstantString:0x100002050
//__NSCFConstantString:0x100002050

这些字符串对象都存储于字符串常量区。对象

OC有五大内存管理区域,地址由小到大分别为:代码段、数据段、BSS段、堆、栈。blog

__NSCFConstantString类型的字符串常量就存储于数据段当中的常量区。

经过stringWithFormat建立NSString

一般在开发中,须要将获得的临时变量或者类对象的字符串进行拼接时会用到这个类方法class method。

那么这种建立方式建立出来的类型是什么呢?它存储的空间地址又在哪里呢?不妨再次打印一下:

NSString* str=[NSString stringWithFormat:@"hi"];
NSLog(@"%@:%p",[str class],str);

//打印以下:
NSTaggedPointerString:0x723673ffe0bc6c91

说到NSTaggedPointerString这个类型,就要说到标签指针(tagged pointer)了,苹果在推出64位架构的A7双核处理器,也就是5s的时候,为了节省内存和提升执行效率,苹果提出了Tagged Pointer的概念来标注特定类型的数值。

标签指针:标签指针在不使用原来类型对象的状况下,把与数值有关的所有消息都放在指针值里面。运行期系统会在消息派发期间检测到这种标签指针。除了NSString外,NSNumber、NSDate类型也会采用该策略。

那么为何原有对象会浪费内存?咱们能够拿NSNumber对象来举个例子。众所周知,NSNumber的占位与CPU的位数有关。在32位机器上整数会占4个字节,64位上则占8个字节。因此直接采起原有对象类型来存储,从32位机迁移到64位机后,NSNumber的对象占用的内存空间就会加倍。

具体存储策略以下图所示:

在64位机器上,咱们能够将采用标签指针策略的对象当作由两个部分的指针组成。第一个部分用来存储特殊标记,表示这是一个采用了标签指针策略的指针(不指向任何内存地址),第二个部分剩余的内存大小均可以用做存储数据。

可是当字符串内容超过了指针范围后,就不会再采起标签指针的策略了。例如初始化一个很长的德语单词:

NSString* str=[NSString stringWithFormat:@"Kraftfahrzeughaftpflichtversicherung"];
NSLog(@"%@->%@->%@->%@:%p",[str class],[[str class] superclass],[[[str class] superclass] superclass],[[[[str class] superclass] superclass] superclass],str);

//打印以下:
__NSCFString->NSMutableString->NSString->NSObject:0x103825090

由此可知,__NSCFString继承于NSMutableString,而NSMutableString又继承于NSString。之因此会出现这种情况也就是由于oc的系统框架中使用了类族的模式。通常状况下能够把该类型直接看作为NSString类型,该对象存储于堆中。

不一样类型的NSString存储位置

类名 存储位置 初始化引用计数
__NSCFString 1
NSTaggedPointerString 2^64-1
__NSCFConstantString 常量区 2^64-1

总结

  • 采用语法糖建立NSString会以单例模式存储于常量区。
  • 当NSString采用stringWithFormat来建立对象时,系统会优先采起标签指针策略来存储对象,当对象的内容和Flag大小超出了指针大小,则采用常规方式存储oc对象。
  • 不管采用什么方式初始化NSString,它的类型都不会是NSString类型,由于系统生成NSString的接口都是隐藏在类族中的公共接口。因此通常不会使用 [str class]==[NSString class] [str isMemberOfClass:[NSString class]]来判断str类型,而是采用 [str isKindOfClass:[NSString class]]来判断。

参考文章:
1.NSTaggedPointerString
2.MemoryAllocation

相关文章
相关标签/搜索