在iOS 的内存管理里面,NSString算是一个比较特殊的情况,它是一个OC对象,可是它的内存管理跟它指向的内容有关。
主要分两种:
(1)NSString 指向一个常量字符串,即在编译时已经确定的值,那么NSString就不受内存管理
(2)除开第一种情况,那么NSString就跟其他OC对象一样,受retainCount控制。
测试代码
NSString *str11 = @"dsfsdfdddd";
NSString *str22 = [NSString stringWithFormat:@"dsfdsfds"];
NSString *str33 = [NSString stringWithString:str22];
NSLog(@"str11:%lu --- str22:%lu --- str33:%lu", [str11 retainCount], [str22 retainCount], [str33 retainCount]);
输出:
str11:18446744073709551615 --- str22:18446744073709551615 --- str33:1
那么试着输出在lldb这几个对象输出这几个对象的class
(lldb) po str11.class
__NSCFConstantString
(lldb) po str22.class
NSTaggedPointerString
(lldb) po str33.class
__NSCFString
第一个str11是一个常量字符串__NSCFConstantString,他指向一个常量,可以这样理解,编译器在编译的时候,把这个变量值@"dsfsdfdddd"添加到常量表里面,常量表里面的变量在APP结束之后才会被释放,指向这块常量表的指针都不受retainCount管理(或者这样理解:所有指向常量的变量在编译时都被替换成常量地址,那么就不存在OC指针的内存释放了)
第二个str22比较特殊,如果stringWithFormat里面直接写一个字符串,那么编译器会编译成一个字符串常量,那么跟第一种情况是一样的(可能个人理解有偏差)。
如果stringWithFormat需要进行拼接,那么stringWithFormat会返回一个mutableString类型
测试代码:把上面str22的代码换成这个
NSString *str22 = [NSString stringWithFormat:@"dsfdsfds%i", 11];
输出:__NSCFString ,retainCount==1
并且可以在调试时候看到str22类型是NSMutableString
第三个str33跟str22类似,如果stringWithString参数是个字符串对象,如stringWithString:str22,那么函数返回结果是一个NSMutableString对象;而如果是指定一个字符串值,如stringWithString:@"dsadsa",那么返回的就是一个__NSCFConstantString,这里应该是编译器在编译时自行优化了
把
NSString *str33 = [NSString stringWithString:@"dsadsa"];
替换成:
NSString *str33 = @"dsadsa";
那就和第一个str11一样的了
另外retain/copy操作对常量字符串无效
比如NSString *str11 = [@"dddd" copy];
str11不会变化,个人理解在 copyWithZone里面,系统会判断对象是不是指向常量。