你所不知道的NSString

来源:互联网 发布:war3 mac 1.26 局域网 编辑:程序博客网 时间:2024/05/29 04:37
在 iOS 和 Mac OS X 应用的开发中,用到最多的类型应该就是 NSString 了吧?但是你能确定你对 NSString 有足够的了解吗?

看看下面的代码,在 ARC 的情况下当然不会有问题,但在非 ARC 的情况下会有内存泄露吗?
for (NSInteger i = 0; i < 10000; ++i) {
NSString *name = [[NSStringalloc]initWithString:@"wenbi"]; NSLog(@"%@", name);}

先使用 Analyze 工具分析代码,这段代码不会有任何错误。
然后使用 Leaks 工具,反复操作后,内存也不会增长。

这说明上面的代码没有内存泄露,这颠覆了我们在内存管理方面普遍遵守的原则:有 alloc 就须有 release,但是上面的代码有 alloc 却不需要 release,为何?

因为,不管你以什么样的方式使用,所有的字符串常量,都存储于程序的静态存储区中。

NSString *a = @"Hello";
NSString *b = [NSStringstringWithString:@"Hello"];
NSString *c = [[NSStringallocinitWithString:@"Hello"];

上面的代码执行完成后,使用断点中断执行,在 Local Variables 视图中查看 a, b, c 三个变量,可以看到以下的结果:



可以看到,虽然变量 a, b, c 以不同的方式初始化为相同的值,但是 a, b, c 都指向同样的内存地址。

对于变量 b 的初始化,编译器会产生一个编译警告:



并且给出了一个推荐的初始化方式,也是就变量 a 的初始化方式:



变量 a, b, c 都指向静态存储区中相同的内存,也就是存放 @"Hello" 字符串的内存。由于静态存储区中的内存地址在程序编译时期就已确定,并且在整个应用程序的生命周期内都会存在,所以对 a, b, c 变量进行任意次的 release 都不会有任何问题。使用 retainCount 可以看到 a, b, c 的引用计数:

使用 NSLog 输出变量 a 的引用计数:
NSLog(@"%u", a.retainCount);

输出:
2013-06-03 17:01:37.689 ch02[1001:c07] 4294967295

也就是 (NSUInteger)-1 的值。

那么,使用 [NSString stringWithFormat:] 和 [[NSString alloc] initWithFormat:] 的情况又会如何呢:
NSString *a = @"Hello";
NSString *b = [NSString stringWithFormat:@"%@"@"Hello"];
NSString *c = [[NSStringallocinitWithFormat:@"%@"@"Hello"];

再查看 a, b, c 变量的情况:



变量 a 会仍然指静态存储区中存放 @"Hello" 的内存,但是 b 和 c 由于使用格式化的方式初始化,编译器在编译时期不能确定 b 和 c 的值,只能在运行时期初始化,所以 b 和 c 分别指向应程序堆中不同的内存。变量 b 为 autorelease 的对象,c 在使用完成后,必须要调用 release 释放内存。