iOS NSString详细用法

一、NSString的内存管理机制

1、众所周知,在Objective-C中的内存管理是通过一种叫做“引用计数器”的机制管理的。举例, 当我们声明了一个新的实例:
NSData *data = [[NSData alloc] init];
NSLog(@"%ld",[data retainCount]);//打印输出为1

如果我们再次引用它的时候,他的引用计数值会+1变为2。使用完毕后需要调用[data release] ,使引用计数-1. 当该值为零的时候,系统会将data实例回收掉并释放内存。

[data release];
2、请问在这种情况下会不会造成内存泄露?为什么不会崩溃?

代码如下:

NSString *str = [[NSString alloc] initWithString:@"ABC"];
str = @"123";
[str release];
NSLog(@"%@".str);
(1)首先,咱们先对这段代码进行分析。
  • 第一句 声明了一个NSString类型的实例 str, 并将其初始化init后赋值为@"ABC"
  • 第二行,将str的指针指向了一个常量@"123"。 理论上讲在第一行初始化的@"ABC"没有任何任何指针指向了。 所以造成了内存泄露
  • 然后第三行, 将str的引用计数-1
  • 第四行输出str的值 为123.
(2)然后回答为什么不会崩溃, 因为第三行的release 实际上是release了一个常量@"123" 而作为常量,其默认的引用计数值是很大的(100k+),不信的话你们可以试试这句:
NSLog(@"retainCount = %d",[@"123" retainCount]);
(3)最终的输出值会是一个很大很大的数。 所以单单一个release是不会将其释放掉的。
(4)最后再回答这样会不会造成内存泄露。理论上是会内存泄漏的。

但是实际上,Objective-C对NSString类型有特殊照顾。所有的NSString的引用计数器默认初始值都会非常非常大。
NSString是一个不可变的字符串对象。这不是表示这个对象声明的变量的值不可变,而是表示它初始化以后,你不能改变该变量所分配的内存中的值,但你可以重新分配该变量所处的内存空间。

二、__NSCFConstantString的解释

那么__NSCFConstantString是什么呢?其实__NSCFConstantString是一个字符串常量,是没有retainCount(引用计数)的,所以没有强指针指向它,它也不会被销毁。那么,怎么获得不是__NSCFConstantString的字符串呢?

NSString *string1 = @"string 1";  
NSString *string2 = [NSString stringWithString:@"string 2"];  
NSString *string3 = [NSString stringWithFormat:@"string 3"];  
NSString *string4 = [[NSString alloc] initWithString:@"string 4"];  
NSString *string5 = [[NSMutableString alloc] initWithString:@"string"];  
NSLog(@"%@:%@",string1,[string1 class]);  
NSLog(@"%@:%@",string2,[string2 class]);  
NSLog(@"%@:%@",string3,[string3 class]);  
NSLog(@"%@:%@",string4,[string4 class]);  
NSLog(@"%@:%@",string5,[string5 class]); 
运行结果:
2015-04-18 10:14:35.587 内存管理[6631:201537] string 1:__NSCFConstantString
2015-04-18 10:14:35.587 内存管理[6631:201537] string 2:__NSCFConstantString
2015-04-18 10:14:35.587 内存管理[6631:201537] string 3:NSTaggedPointerString
2015-04-18 10:14:35.587 内存管理[6631:201537] string 4:__NSCFConstantString
2015-04-18 10:14:35.588 内存管理[6631:201537] string5:__NSCFString
通过以上的运行结果,发现只有使用[[NSMutableString alloc] initWithString:]得到的NSString才是__NSCFString。

三、NSString特性分析学习

我们都知道NSString是一个Objective-C的类,但是我们有时发现它的对象在内存管理上貌似和其他的对象有一些区别。比如有时你会发现对一个NSString进行copy操作时,它还是原本的对象,实际上并未拷贝对象。本博客就来研究下这个问题。

1.NSString内存管理特性分析
1.1 准备

为了方便测试,我先写了个宏,用来打印NSString的isa、内存地址、值、retainCount。 注:为了了解内存特性,后面的代码都使用了手动内存管理。

#define TLog(_var) ({ NSString *name = @#_var; NSLog(@"%@: %@ -> %p : %@  %d", name, [_var class], _var, _var, (int)[_var retainCount]); })
1.2 NSString的创建
1.2.1测试NSString

在objc中,我们一般通过几种方法来创建NSString呢,一般有三种方法,现在我们就分别对这三种情况写段测试代码,如下:

NSString *str1 = @"1234567890";    TLog(str1);
//str1: __NSCFConstantString -> 0x715ec : 1234567890  -1
NSString *str2 = [NSString stringWithString:@"1234567890"];        TLog(str2);
//str2: __NSCFConstantString -> 0x715ec : 1234567890  -1
NSString *str3 = [NSString stringWithFormat:@"1234567890"];        TLog(str3);
//str3: __NSCFString -> 0x1557cb50 : 1234567890  1

看到上面这段测试代码,我们可以发现几点同我们想象不同的地方:

  • 第一种方式和第二种方式创建出来的NSString时一模一样的,isa是__NSCFConstantString,内存地址一样,retainCount是-1.
  • 第三种方式创建的NSString和创建其他objc对象类似的,在堆上分配内存,初始retainCount为1.
    这里面有几个疑问:
  • 什么是__NSCFConstantString?
  • 为什么第一种和第二种NSString的内存地址是一样的?
  • 为什么他们的retainCount是-1?
1.2.2 NSString创建的写法

其实上面第一种写法和第二种写法是完全一样的,没有任何区别,从iosSDK6开始,第二种写法已经被遗弃了,如果用第二种写法创建NSString,编译器就会报一个警告。

1.2.3 retainCount为-1是什么情况

1、首先retainCount是NSUInteger的类型,其实上面的打印是将它作为int类型打印。所以它其实不是-1,它的实际值是4294967295。
2、在objc的retainCount中.如果对象的retainCount为这个值,就意味着“无限的retainCount”,这个对象是不能被释放的。
3、所有的 __NSCFConstantString对象的retainCount都为-1,这就意味着 __NSCFConstantString不会被释放,使用第一种方法创建的NSString,如果值一样,无论写多少遍,都是同一个对象。而且这种对象可以直接用 == 来比较

NSString *str1 = @"1234567890";    TLog(str1);
//str1: __NSCFConstantString -> 0x715ec : 1234567890  -1
NSString *str2 = @"1234567890";    TLog(str2);
//str2: __NSCFConstantString -> 0x715ec : 1234567890  -1
assert(@"abc"==@"abc"); //一直正确
1.3 NSString的retain、copy和mutableCopy

我们写一段代码分别对 __NSCFConstantString 和 __NSCFString 进行retain和copy测试

NSString *str1 = @"a";    TLog(str1);
NSString *str2 = [str1 retain];  TLog(str2);
NSString *str3 = [str1 copy]; TLog(str3);
NSString *str4 = [str1 mutableCopy]; TLog(str4);

/*
 str1: __NSCFConstantString -> 0x7c5e0 : a  -1
 str2: __NSCFConstantString -> 0x7c5e0 : a  -1
 str3: __NSCFConstantString -> 0x7c5e0 : a  -1
 str4: __NSCFString -> 0x1559eb80 : a  1
*/

上面的测试可以看出,对一个__NSCFConstantString进行retain和copy操作都还是自己,没有任何变化,对其mutableCopy操作可将其拷贝到堆上,retainCount为1.

NSString *str1 = [@"a" mutableCopy];    TLog(str1);
NSString *str2 = [str1 retain];  TLog(str2);
NSString *str3 = [str1 copy]; TLog(str3);
NSString *str4 = [str1 mutableCopy]; TLog(str4);

/*
str1: __NSCFString -> 0x17d6d280 : a  1
str2: __NSCFString -> 0x17d6d280 : a  2
str3: __NSCFConstantString -> 0x3bd40090 : a  -1
str4: __NSCFString -> 0x17e684d0 : a  1
*/

上面的测试中,我们发现,对__NSCFString进行retain和mutableCopy操作时,其特性符合正常的对象特性。但是对其copy时,它却变成了一个__NSCFConstantString对象!为了确定什么情况下才会出现这种现象我们多做一些测试

NSString *str1 = [[@"a" mutableCopy] copy];    TLog(str1);
NSString *str2 = [NSString stringWithFormat:@"%s","a"];  TLog(str2);
NSString *str3 = [[[@"path/a" lastPathComponent] mutableCopy] copy]; TLog(str3);

NSString *str4 = [[@"b" mutableCopy] copy]; TLog(str4);
NSString *str5 = [[@"c" mutableCopy] copy]; TLog(str5);
NSString *str6 = [[@"d" mutableCopy] copy]; TLog(str6);
NSString *str7 = [[@"e" mutableCopy] copy]; TLog(str7);
NSString *str8 = [[@"f" mutableCopy] copy]; TLog(str8);

NSString *str9 = [[@"\\" mutableCopy] copy]; TLog(str9);
NSString *str10 = [[@"$" mutableCopy] copy]; TLog(str10);
NSString *str11 = [[@"." mutableCopy] copy]; TLog(str11);
NSString *str12 = [[@"aa" mutableCopy] copy]; TLog(str12);
/*
str1: __NSCFConstantString -> 0x3bd40090 : a  -1
str2: __NSCFConstantString -> 0x3bd40090 : a  -1
str3: __NSCFConstantString -> 0x3bd40090 : a  -1
str4: __NSCFString -> 0x175ab390 : b  1
str5: __NSCFString -> 0x176a5ce0 : c  1
str6: __NSCFString -> 0x175ab960 : d  1
str7: __NSCFString -> 0x176a5cc0 : e  1
str8: __NSCFString -> 0x176a5d50 : f  1
str9: __NSCFString -> 0x176a5d60 : \  1
str10: __NSCFString -> 0x176a6700 : $  1
str11: __NSCFString -> 0x175ab750 : .  1
str12: __NSCFString -> 0x175ab760 : aa  1
*/NSString *str1 = [[@"a" mutableCopy] copy];    TLog(str1);
NSString *str2 = [NSString stringWithFormat:@"%s","a"];  TLog(str2);
NSString *str3 = [[[@"path/a" lastPathComponent] mutableCopy] copy]; TLog(str3);

NSString *str4 = [[@"b" mutableCopy] copy]; TLog(str4);
NSString *str5 = [[@"c" mutableCopy] copy]; TLog(str5);
NSString *str6 = [[@"d" mutableCopy] copy]; TLog(str6);
NSString *str7 = [[@"e" mutableCopy] copy]; TLog(str7);
NSString *str8 = [[@"f" mutableCopy] copy]; TLog(str8);

NSString *str9 = [[@"\\" mutableCopy] copy]; TLog(str9);
NSString *str10 = [[@"$" mutableCopy] copy]; TLog(str10);
NSString *str11 = [[@"." mutableCopy] copy]; TLog(str11);
NSString *str12 = [[@"aa" mutableCopy] copy]; TLog(str12);
/*
str1: __NSCFConstantString -> 0x3bd40090 : a  -1
str2: __NSCFConstantString -> 0x3bd40090 : a  -1
str3: __NSCFConstantString -> 0x3bd40090 : a  -1
str4: __NSCFString -> 0x175ab390 : b  -1
str5: __NSCFString -> 0x176a5ce0 : c  -1
str6: __NSCFString -> 0x175ab960 : d - 1
str7: __NSCFString -> 0x176a5cc0 : e  -1
str8: __NSCFString -> 0x176a5d50 : f - 1
str9: __NSCFString -> 0x176a5d60 : \  -1
str10: __NSCFString -> 0x176a6700 : $  -1
str11: __NSCFString -> 0x175ab750 : .  -1
str12: __NSCFString -> 0x175ab760 : aa  -1
*/

2. 小结

经过这一系列的测试分析,让我们认识了__NSCFConstantString以及它的一些特性,它是在编译时就决定的,不能在运行时创建。

3. NSString属性什么时候用copy,什么时候用strong?

http://southpeak.github.io/2015/05/10/ios-techset-1/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容

  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,114评论 29 470
  • 1.项目经验 2.基础问题 3.指南认识 4.解决思路 ios开发三大块: 1.Oc基础 2.CocoaTouch...
    阳光的大男孩儿阅读 4,966评论 0 13
  • iOS面试题及答案 1. Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一...
    iOS_阿辉阅读 1,905评论 0 32
  • 最近一朋友正准备跳槽,就从各处搜索整理一些基础,便于朋友复习,也便于自己复习查看. 1. 回答person的ret...
    smile丽语阅读 1,714评论 0 7
  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 1,935评论 1 16