前瞻
在 Objective-C 的 Fondation 框架中 NSString 对象是很复杂的存在,各种方式创建以及不同长度的字符串都会影响 NSString 对象在内存中所处的位置。Objective-C 在运行时也对其做了很多优化。今天就来研究一下 NSString 这个复杂的对象。
NSString 类型的出现
- 参考学长的博客,我们以四个字符串为例,分别用 @ 直接创建两个长短不一的,再分别用 stringWithFormat 创建两个长短不一的
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* string1 = @"hello3GiOS";
NSString* string2 = @"hello";
NSString* string3 = [NSString stringWithFormat:@"hello3GiOS"];
NSString* string4 = [NSString stringWithFormat:@"hello"];
NSLog(@" \n%@ %p\n, %@ %p\n, %@ %p\n, %@ %p\n, ", [string1 class], string1, [string2 class], string2, [string3 class], string3, [string4 class], string4 );
}
return 0;
}
- 我们发现对于用@ 直接创建的字符串的类型与长短无关
- 但是用stringWithFormat 创建的字符串产生的类型就和长短有关
__NSCFConstantString
对于这个类型的NSString,我们测试打印他的retainCount
NSString* string1 = @"hello3GiOS";
NSString* string2 = @"hello";
NSLog(@" string1的引用计数: %ld", CFGetRetainCount((__bridge CFTypeRef)(string1)));
NSLog(@" string2的引用计数: %ld", CFGetRetainCount((__bridge CFTypeRef)(string2)));
- 发现二者的引用计数是很大很大的数字,我们是ARC环境下(也即是自动释放池)进行测试的,如此一来说明他们不能被释放。
它是创建之后便是放不掉的对象 我们创建两个内容相同的对象试试
NSString* string1 = @"hello3GiOS";
NSString* string = @"hello3GiOS";
NSLog(@"string1的地址:%p, string的地址 %p", string1, string);
- 上面创建了两个内容,类型相同的字符串,结果发现他们的地址是相同的,这也就说明了 __NSCFConstantString 是一种创建以后无法被释放的单例
怎么出现__NSCFConstantString
- 查阅资料
-
- 上面测试的@“…” 可以产生 __NSCFConstantString
-
- CFSTR(“…”) 可以产生 __NSCFConstantString
-
- stringWithString 可以产生 __NSCFConstantString (此方法等同于@“…”) 可以忽略。
__NSCFString
- 同样的打印 __NSCFString 的引用计数 和类型来测试
NSString* string3 = [NSString stringWithFormat:@"hello3GiOS"];
NSString* textString = [NSString stringWithFormat:@"hello3GiOS"];
NSLog(@"string3的地址:%p 类型 %@, textString的地址 %p 类型 %@", string3,[string3 class], textString, [textString class]);
- 类型和地址
-
- 创建两个内容一样的 __NSCFString 他们的地址是不一样的
- 引用计数
- 查阅资料
- 和 __NSCFConstantString 不同, __NSCFString 对象是在运行时创建的一种 NSString 子类,他并不是一种字符串常量。所以和其他的对象一样在被创建时获得了 1 的引用计数。
通过 NSString 的 stringWithFormat 等方法创建的 NSString 对象一般都是这种类型(长度大于等于9)
NSTaggedPointerString
NSString* string4 = [NSString stringWithFormat:@"hello"];
NSString* testString = [NSString stringWithFormat:@"hello"];
NSLog(@" string4的引用计数: %ld", CFGetRetainCount((__bridge CFTypeRef)(string4)));
NSLog(@" test的引用计数: %ld", CFGetRetainCount((__bridge CFTypeRef)(testString)));
NSLog(@"string4的地址:%p 类型 %@, teststring的地址 %p 类型 %@", string4, [string4 class], testString, [testString class]);
- 可以看到NSTaggedPointerString 也是一种单例并且一旦创建就不会被释放
- 查阅资料
- 对于 NSString 对象来讲,当非字面值常量的数字,英文字母字符串的长度小于等于 9 的时候会自动成为 NSTaggedPointerString 类型,如果有中文或其他特殊符号(可能是非 ASCII 字符)存在的话则会直接成为 )__NSCFString 类型。
- 转载博客
- 理解这个类型,需要明白什么是标签指针,这是苹果在 64 位环境下对 NSString,NSNumber 等对象做的一些优化。简单来讲可以理解为把指针指向的内容直接放在了指针变量的内存地址中,因为在 64 位环境下指针变量的大小达到了 8 位足以容纳一些长度较小的内容。于是使用了标签指针这种方式来优化数据的存储方式,在运行时根据实际情况创建。
|