NSString的三种实现方式
OC这个语言在不停的升级自己的内存管理,尽量的让自己的
OC的字符串
问题引入
在学习字符串的过程中间会遇到一个因为OC语言更新造成的问题
例如:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* str1 = [NSString stringWithFormat:@"hello"];
NSString* str2 = [NSString stringWithFormat:@"hello"];
NSString* str3 = @"hello";
NSLog(@"%d", str1 == str2);
NSLog(@"%d", str2 == str3);
}
return 0;
}
按照书上的老版本来说的话,我们这里应该输出两个0才对,因为一个第一个和第二个都是堆区创建的他们应该是不一样的,但是结果却出乎我们的意料,我们这里在堆区中创建的两个字符串的指针地址是一样的。结果输出了1。下面我们就解决一下这个问题。
我们这时候输出一下这几个字符串的类型,这里就出现了这个NSTaggedPointerString这个类型,这时候我们会感到困惑,为什么我们明明是一个NSString类型,但是为什么会有这几种情况呢?这里涉及到一个类簇的问题,但是这里就简单理解成我们在这个NSString这个大类下有三种小的类别来管理这个类型,从而让字符串有一个更好的储存方式来保障我们的内存使用。
这时候,我们来正式介绍一下在OC中间有关字符串的几种实现方式。
OC中字符串NSString
有三种实现方式,分别为
__NSCFConstantString,
__NSCFString,
NSTaggedPointerString.
现在我们通过一段代码来分析这三种字符串的差异
代码示例
我们给出一段代码来分析一下有关这三种字符串的内容:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* str1 = @"hello";
NSString* str2 = [NSString stringWithFormat:@"helloworld"];
NSString* str3 = [NSString stringWithFormat:@"神"];
NSString* str4 = [NSString stringWithFormat:@"hello"];
NSLog(@"%lu, %@", [str1 retainCount], [str1 class]);
NSLog(@"%lu, %@", [str2 retainCount], [str2 class]);
NSLog(@"%lu, %@", [str3 retainCount], [str3 class]);
NSLog(@"%lu, %@", [str4 retainCount], [str4 class]);//输出他的一个引用计数和一个类型
}
return 0;
}
这是这个的输出结果:
我们现在就了解一下这三种字符串
__NSCFConstantString类型
- 这个字符串类型是在常量区创建的一个字符串
- 这个字符串是一种编译时的常量
- 我们可以用
@""
和stringWithString
方式创建 - 打印的
retainCount
的值是很大的,无法通过release方式去释放,是一个单例模式
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* str1 = @"hello";
NSString* str2 = [NSString stringWithFormat:@"helloworld"];
NSString* str3 = [NSString stringWithFormat:@"神"];
NSString* str4 = [NSString stringWithFormat:@"hello"];
NSLog(@"%lu, %@", [str1 retainCount], [str1 class]);
//NSLog(@"%lu, %@", [str2 retainCount], [str2 class]);
//NSLog(@"%lu, %@", [str3 retainCount], [str3 class]);
//NSLog(@"%lu, %@", [str4 retainCount], [str4 class]);
[str1 release];
NSLog(@"%lu, %@", [str1 retainCount], [str1 class]);
}
return 0;
}
这里我们进行了一次release操作,按道理来说我们应该是把对他进行了一次释放的,但是打印结果是
这里发现我们无法对其进行释放,所以我们可以认为他是一个单例。
__NSCFString
- 这个字符串类型是一个在堆区创建的一个字符串
- 这个字符串是在运行的时候创建的
- 较长字符串会自动转化成这个类型
- 中文字符串也会自动转成这个类型
这是他的引用计数:
从引用计数可以看出他不是一个单例,可以被释放。
借用一段学长的话来对于这个字符串类型进行一个讲解:
即使两个对象的内容相同,它们在堆上的内存地址也是不同的。每个对象都在独立的内存空间中存储,具有自己的地址。这意味着通过不同的对象引用访问这两个对象时,实际上访问的是不同的内存地址。
NSTaggerPointerSring
这个字符串类型是最新的一个字符串类型,他也是在堆区创建的但是他可用用来存储一下较短字符串,实现一个节约内存的效果,引用一段学长的话
TaggedPointer的意思是标签指针,这是苹果在 64 位环境下对 NSString,NSNumber
等对象做的一些优化。简单来讲可以理解为把指针指向的内容直接放在了指针变量的内存地址中,因为在 64 位环境下指针变量的大小达到了 8
位足以容纳一些长度较小的内容。于是使用了标签指针这种方式来优化数据的存储方式。从他的引用计数可以看出,这货也是一个释放不掉的单例常量对象。在运行时根据实际情况创建。
这里是有关他的引用计数:
从这里可以看出他是一个单例。
也是通过一个stringWithFormat方式,但是由于较短英文字符串,所以可以用地址来直接存储较短英文字符串的数值,这是一个单例。
- 是一个单例
- 是在堆区创建的一个字符串
- 可以将其当作一个伪对象,对象直接被存储在指针的地址上面
- 较短且在堆区创建的字符串会呈现出这个样式(长度小于9)
总结
我们这里简单介绍了三种字符串的实现方式,这里只是简单的介绍了这三种字符串的格子的一个特点,没有深入的去研究它的底层代码,我们这里主要需要理解在不同的情况下,字符串的创建出的类别是不一样的。