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] str6 address = 0x100203a70 9 Program ended with exit code: 0
结果一目了然。不可变字符串的使用copy创建的新副本无论赋值给可变还是不可变字符串,都不会分配新的内存空间。