探讨NSString和NSMutableString的内存问题以及copy和MutableCopy两个方法

Posted SharpeyeKardel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了探讨NSString和NSMutableString的内存问题以及copy和MutableCopy两个方法相关的知识,希望对你有一定的参考价值。

NSString:
 1 //main.m
 2 #import <Foundation/Foundation.h>
 3 
 4 int main(int argc, const char * argv[]) {
 5     @autoreleasepool {
 6         
 7         NSString *str1 = @"aaa";
 8         NSString *str2 ;
 9         NSString *str3 ;
10         NSString *str4 ;
11         NSString *str5 ;
12         NSString *str6 ;
13         
14         
15         
16         str2 = [NSString stringWithString:str1];
17         str3 = str1;
18         str4 = [[NSString alloc]initWithString:str1];
19         str5 = [str1 copy];
20         str6 = [str1 mutableCopy];
21         
22         NSLog(@"@\"aaa\" address = %p", @"aaa");    // 输出字符串aaa的地址。
23         NSLog(@"change before:");
24         NSLog(@" str1 = %@", str1);           // 输出字符串str1的内容
25         NSLog(@" str1 address = %p", str1);    // 输出字符串str1的内容所在地址。
26         NSLog(@" str2 = %@", str2);           // 输出字符串str2的内容
27         NSLog(@" str2 address = %p", str2);    // 输出字符串str2的内容所在地址。
28         NSLog(@" str3 = %@", str3);           // 输出字符串str3的内容
29         NSLog(@" str3 address = %p", str3);    // 输出字符串str3的内容所在地址。
30         NSLog(@" str4 = %@", str4);           // 输出字符串str4的内容
31         NSLog(@" str4 address = %p", str4);    // 输出字符串str4的内容所在地址。
32         NSLog(@" str5 = %@", str5);           // 输出字符串str5的内容
33         NSLog(@" str5 address = %p", str5);    // 输出字符串str5的内容所在地址。
34         NSLog(@" str6 = %@", str6);           // 输出字符串str6的内容
35         NSLog(@" str6 address = %p", str6);    // 输出字符串str6的内容所在地址。
36         
37         NSLog(@" ");
38         NSLog(@"@\"bbb\" address = %p", @"bbb");
39         
40         NSLog(@"change after:");
41         str1 = @"bbb";
42         NSLog(@" str1 = %@", str1);      //输出修改后str1的内容
43         NSLog(@" str1 address = %p", str1);  //输出修改后str1的内容所在地址
44         NSLog(@" str2 = %@", str2);    //输出修改后str2的内容
45         NSLog(@" str2 address = %p", str2);  //输出修改后str2的内容所在地址
46         NSLog(@" str3 = %@", str3);   //输出修改后str3的内容
47         NSLog(@" str3 address = %p", str3);  //输出修改后str3的内容所在地址
48         NSLog(@" str4 = %@", str4);  //输出修改后str4的内容
49         NSLog(@" str4 address = %p", str4);  //输出修改后str4的内容所在地址
50         NSLog(@" str5 = %@", str5);  //输出修改后str5的内容
51         NSLog(@" str5 address = %p", str5);  //输出修改后str5的内容所在地址
52         NSLog(@" str6 = %@", str6);  //输出修改后str6的内容
53         NSLog(@" str6 address = %p", str6);  //输出修改后str6的内容所在地址
54         
55         NSLog(@" @\"aaa\" address = %p", @"aaa"); //再次输出修改后字符串aaa的地址
 运行结果:

1
2016-06-05 14:29:49.451 copyTest[1526:60199] @"aaa" address = 0x100001050 2 2016-06-05 14:29:49.452 copyTest[1526:60199] change before: 3 2016-06-05 14:29:49.452 copyTest[1526:60199] str1 = aaa 4 2016-06-05 14:29:49.452 copyTest[1526:60199] str1 address = 0x100001050 5 2016-06-05 14:29:49.453 copyTest[1526:60199] str2 = aaa 6 2016-06-05 14:29:49.453 copyTest[1526:60199] str2 address = 0x100001050 7 2016-06-05 14:29:49.453 copyTest[1526:60199] str3 = aaa 8 2016-06-05 14:29:49.453 copyTest[1526:60199] str3 address = 0x100001050 9 2016-06-05 14:29:49.453 copyTest[1526:60199] str4 = aaa 10 2016-06-05 14:29:49.453 copyTest[1526:60199] str4 address = 0x100001050 11 2016-06-05 14:29:49.453 copyTest[1526:60199] str5 = aaa 12 2016-06-05 14:29:49.453 copyTest[1526:60199] str5 address = 0x100001050 13 2016-06-05 14:29:49.453 copyTest[1526:60199] str6 = aaa 14 2016-06-05 14:29:49.453 copyTest[1526:60199] str6 address = 0x100203930 15 2016-06-05 14:29:49.453 copyTest[1526:60199] 16 2016-06-05 14:29:49.453 copyTest[1526:60199] @"bbb" address = 0x100001270 17 2016-06-05 14:29:49.453 copyTest[1526:60199] change after: 18 2016-06-05 14:29:49.453 copyTest[1526:60199] str1 = bbb 19 2016-06-05 14:29:49.453 copyTest[1526:60199] str1 address = 0x100001270 20 2016-06-05 14:29:49.453 copyTest[1526:60199] str2 = aaa 21 2016-06-05 14:29:49.453 copyTest[1526:60199] str2 address = 0x100001050 22 2016-06-05 14:29:49.454 copyTest[1526:60199] str3 = aaa 23 2016-06-05 14:29:49.454 copyTest[1526:60199] str3 address = 0x100001050 24 2016-06-05 14:29:49.454 copyTest[1526:60199] str4 = aaa 25 2016-06-05 14:29:49.454 copyTest[1526:60199] str4 address = 0x100001050 26 2016-06-05 14:29:49.454 copyTest[1526:60199] str5 = aaa 27 2016-06-05 14:29:49.454 copyTest[1526:60199] str5 address = 0x100001050 28 2016-06-05 14:29:49.454 copyTest[1526:60199] str6 = aaa 29 2016-06-05 14:29:49.454 copyTest[1526:60199] str6 address = 0x100203930 30 2016-06-05 14:29:49.454 copyTest[1526:60199] @"aaa" address = 0x100001050 31 Program ended with exit code: 0

结果分析:

(1)在定义str1时就先把字符串@"aaa"赋值给str1,输出时 @"aaa"和str1的地址相同,都是 0x100001050 。

(2)接着以三种不同的方式将str1赋值给str2, str3, str4

(3)再次给str1赋值,这次将字符串@"bbb"赋值给str1。 不同的是,我们在赋值前先输出一次字符串@"bbb"的地址,根据输出结果发现又与str1的地址相同,但地址已经发生变化 0x100001050 --> 0x100001270 。由此可以发现,不可变字符串NSString在第一次赋值时,指向一个内存空间,当它被再次被赋值于不同的字符串时,它会指向另一个内存空间,这与可变字符串NSMutableString不同(稍后会对NSMutableString进行测试)。

不可变字符串NSString的“不可变”我理解为是它引用的地址的那个内容不能改变,但是它引用的地址是可以改变的,有点拗口:

我把aaa赋值给str1,没有改变aaa这个字符串,当然也没有改变str1。当我再次赋值bbb给str1时,似乎是str1引用的地址-0x100001050 的内容(即aaa)改变成了bbb,其实不然。事实上我们是改变了str1引用的地址来实现这种“假象”的。str1的引用地址已经由 0x100001050(含有字符串aaa的地址)变为 0x100001270(含有字符串bbb的地址)。我们通过最后一行输出结果:字符串aaa的地址仍是 0x100001050,就可以知道在str1被bbb重新赋值的这个过程中aaa没有发生任何改变。

(4)str2,str3,str4的结果就很清晰了。他们都只是引用了aaa赋值给str1时的地址。所以他们都引用这个地址0x100001050,这个地址里面的内容也就是aaa了。

(5)str5,str6分别是str1使用copy和MutableCopy方法创建的副本。 根据输出可以看到在str1改变前,通过copy创建的副本str5引用的地址,与str1引用的地址相同-0x100001050;MutableCopy创建的副本引用的地址(0x100203930)则与str1的(0x100001050)不同。可以得出结论:对于不可变字符串NSString,copy方法创建的新副本不会分配新的内存空间,而MutableCopy创建的新副本会分配新的内存空间。

 

NSMutableString:

 

 1 //main.m     
 2 #import <Foundation/Foundation.h>
 3 
 4 int main(int argc, const char * argv[]) {
 5     @autoreleasepool {
 6 
 7         NSMutableString *str1 = [[NSMutableString alloc]initWithCapacity:0];
 8         NSMutableString *str2;
 9         NSMutableString *str3;
10         NSMutableString *str4;
11         NSMutableString *str5;
12         NSMutableString *str6;
13         NSMutableString *str7;
14         
15         
16         NSLog(@"@\"a\" address = %p", @"aaa");
17         NSLog(@"change before:");
18         str1 = [NSMutableString stringWithString:@"aaa"];
19         
20         str2 = str1;
21         str3 = [NSMutableString stringWithString:str1];
22         str4 = [[NSMutableString alloc]initWithString:str1];
23         str5 = [[NSMutableString alloc]initWithCapacity:0];
24         [str5 setString:str1];
25         str6 = [str1 copy];
26         str7 = [str1 mutableCopy];
27         
28         NSLog(@"str1 = %@", str1);            //输出str1内容
29         NSLog(@"str1 address = %p", str1);    //输出str1内容所在的地址
30         NSLog(@"str2 = %@", str2);            //输出str2内容
31         NSLog(@"str2 address = %p", str2);    //输出str2内容所在的地址
32         NSLog(@"str3 = %@", str3);            //输出str3内容
33         NSLog(@"str3 address = %p", str3);       //输出str3内容所在的地址
34         NSLog(@"str4 = %@", str4);            //输出str4内容
35         NSLog(@"str4 address = %p", str4);   //输出str4内容所在的地址
36         NSLog(@"str5 = %@", str5);          //输出str5内容
37         NSLog(@"str5 address = %p", str5);   //输出str5内容所在的地址
38         NSLog(@"str6 = %@", str6);          //输出str6内容
39         NSLog(@"str6 address = %p", str6);   //输出str6内容所在的地址
40         NSLog(@"str7 = %@", str7);           //输出str7内容
41         NSLog(@"str7 address = %p", str7);    //输出str7内容所在的地址
42        
43         NSLog(@" ");
44         NSLog(@"@\"bbb\" address = %p", @"bbb");
45         NSLog(@"change after:");
46         [str1 setString:@"bbb"];
47         NSLog(@"str1 = %@", str1);            //输出修改后str1内容
48         NSLog(@"str1 address = %p", str1);    //输出修改后str1内容所在地址
49         NSLog(@"str2 = %@", str2);             //输出修改后str2内容
50         NSLog(@"str2 address = %p", str2);     //输出修改后str2内容所在地址
51         NSLog(@"str3 = %@", str3);            //输出修改后str3内容
52         NSLog(@"str3 address = %p", str3);     //输出修改后str3内容所在地址
53         NSLog(@"str4 = %@", str4);           //输出修改后str4内容
54         NSLog(@"str4 address = %p", str4);     //输出修改后str4内容所在地址
55         NSLog(@"str5 = %@", str5);           //输出修改后str5内容
56         NSLog(@"str5 address = %p", str5);   //输出修改后str5内容所在地址
57         NSLog(@"str6 = %@", str6);             //输出修改后str6内容
58         NSLog(@"str6 address = %p", str6);     //输出修改后str6内容所在地址
59         NSLog(@"str7 = %@", str7);             //输出修改后str7内容
60         NSLog(@"str7 address = %p", str7);     //输出修改后str7内容所在地址
61         
62     }
63     return 0;
64 }     
运行结果:

1
2016-06-05 15:08:03.191 copyTest[1709:76858] @"a" address = 0x100001068 2 2016-06-05 15:08:03.192 copyTest[1709:76858] change before: 3 2016-06-05 15:08:03.192 copyTest[1709:76858] str1 = aaa 4 2016-06-05 15:08:03.192 copyTest[1709:76858] str1 address = 0x1002001d0 5 2016-06-05 15:08:03.192 copyTest[1709:76858] str2 = aaa 6 2016-06-05 15:08:03.192 copyTest[1709:76858] str2 address = 0x1002001d0 7 2016-06-05 15:08:03.192 copyTest[1709:76858] str3 = aaa 8 2016-06-05 15:08:03.193 copyTest[1709:76858] str3 address = 0x1002002f0 9 2016-06-05 15:08:03.193 copyTest[1709:76858] str4 = aaa 10 2016-06-05 15:08:03.193 copyTest[1709:76858] str4 address = 0x103000000 11 2016-06-05 15:08:03.193 copyTest[1709:76858] str5 = aaa 12 2016-06-05 15:08:03.193 copyTest[1709:76858] str5 address = 0x100200350 13 2016-06-05 15:08:03.193 copyTest[1709:76858] str6 = aaa 14 2016-06-05 15:08:03.193 copyTest[1709:76858] str6 address = 0x61616135 15 2016-06-05 15:08:03.193 copyTest[1709:76858] str7 = aaa 16 2016-06-05 15:08:03.193 copyTest[1709:76858] str7 address = 0x1002004c0 17 2016-06-05 15:08:03.193 copyTest[1709:76858] 18 2016-06-05 15:08:03.193 copyTest[1709:76858] @"bbb" address = 0x1000012a8 19 2016-06-05 15:08:03.193 copyTest[1709:76858] change after: 20 2016-06-05 15:08:03.193 copyTest[1709:76858] str1 = bbb 21 2016-06-05 15:08:03.193 copyTest[1709:76858] str1 address = 0x1002001d0 22 2016-06-05 15:08:03.193 copyTest[1709:76858] str2 = bbb 23 2016-06-05 15:08:03.193 copyTest[1709:76858] str2 address = 0x1002001d0 24 2016-06-05 15:08:03.193 copyTest[1709:76858] str3 = aaa 25 2016-06-05 15:08:03.194 copyTest[1709:76858] str3 address = 0x1002002f0 26 2016-06-05 15:08:03.194 copyTest[1709:76858] str4 = aaa 27 2016-06-05 15:08:03.194 copyTest[1709:76858] str4 address = 0x103000000 28 2016-06-05 15:08:03.194 copyTest[1709:76858] str5 = aaa 29 2016-06-05 15:08:03.194 copyTest[1709:76858] str5 address = 0x100200350 30 2016-06-05 15:08:03.194 copyTest[1709:76858] str6 = aaa 31 2016-06-05 15:08:03.194 copyTest[1709:76858] str6 address = 0x61616135 32 2016-06-05 15:08:03.194 copyTest[1709:76858] str7 = aaa 33 2016-06-05 15:08:03.194 copyTest[1709:76858] str7 address = 0x1002004c0 34 Program ended with exit code: 0

 结果分析:

一、改变str1前:

(1)str2 = str1这种简单的赋值只是str2引用了str1引用的地址,所以地址相同-0x1002001d0。

(2)str3,str4,str5都分配了新的地址空间,3者的地址都不相同。在它们得到了新分配的地址后,以可变字符串str1为参数进行了赋值,但实质上只是提取str1引用的地址里面的内容,即字符串aaa,它们之间没有任何联系。

(3)str6,str7是str1分别用copy和MutableCopy方法创建的新副本,它们引用的地址都和str1的不同,且str6和str7也不同。这里与可变字符串NSString有差异,可见上文结果分析(5)。这里得出结论:可变字符串NSMutableString,使用copy和MutableCopy创建的新副本都会为它们分配新的内存空间。

二、改变str1后:

(1)str1和str2引用了同一个地址,str2自然随着str1的改变而改变。str1的内容就是str2的内容。但是我们这次要观察的重点是,str1的内容变了,但它的地址没有改变,修改前后都是-0x1002001d0。这里与可变字符串NSString有差异,可见上文结果分析(3)。

(2)str3,str4,str5,str6,str7都与str1不同,所以内容不会发生改变。

 

我们再做一个测试:

 1 //main.m
 2 
 3 #import <Foundation/Foundation.h>
 4 
 5 int main(int argc, const char * argv[]) {
 6     @autoreleasepool {
 7 
 8         NSString *str1 = @"a";
 9         NSMutableString *str2;
10         NSMutableString *str3;
11         
12         NSMutableString *str4 = [NSMutableString stringWithString:@"a"];
13         NSString *str5;
14         NSString *str6;
15  
16         //一个不可变字符串str1使用copy和MutableCopy创建两个新副本给2个可变字符串str2和str3
17         str2 = [str1 copy];
18         str3 = [str1 mutableCopy];
19         
20         //一个可变字符串str4使用copy和MutableCopy创建两个新副本给2个不可变字符串str5和str6
21         str5 = [str4 copy];
22         str6 = [str4 mutableCopy];
23         
24         NSLog(@"str1 address = %p", str1);
25         NSLog(@"str2 address = %p", str2);
26         NSLog(@"str3 address = %p", str3);
27         NSLog(@"str4 address = %p", str4);
28         NSLog(@"str5 address = %p", str5);
29         NSLog(@"str6 address = %p", str6);
30     
31     }
32     return 0;
33 }
1 //运行结果:
2 
3 2016-06-05 16:57:01.697 copyTest[2175:117425] str1 address = 0x100001050
4 2016-06-05 16:57:01.698 copyTest[2175:117425] str2 address = 0x100001050
5 2016-06-05 16:57:01.698 copyTest[2175:117425] str3 address = 0x100203980
6 2016-06-05 16:57:01.698 copyTest[2175:117425] str4 address = 0x1002037b0
7 2016-06-05 16:57:01.698 copyTest[2175:117425] str5 address = 0x6115
8 2016-06-05 16:57:01.698 copyTest[2175:117425

以上是关于探讨NSString和NSMutableString的内存问题以及copy和MutableCopy两个方法的主要内容,如果未能解决你的问题,请参考以下文章

探讨NSString在哪个内存区

访问属性时出现 EXC_BAD_ACCESS 异常

关于出现Precompiled header uses __DATE__ or __TIME__警告的探讨和解决方案。

关于出现Precompiled header uses __DATE__ or __TIME__警告的探讨和解决方案。

NSString 到 CFStringRef 和 CFStringRef 到 ARC 中的 NSString?

NSString 和 NSMutableString