• 探讨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] str6 address = 0x100203a70
    9 Program ended with exit code: 0

    结果一目了然。不可变字符串的使用copy创建的新副本无论赋值给可变还是不可变字符串,都不会分配新的内存空间。

  • 相关阅读:
    How to resolve mysql problem when you get code 2003(10061) and 1130
    Windows connect to mysql failed: can't get hostname for your address
    Python基础教程总结(二)
    Python基础教程总结(一)
    Begin to study Deep Learning
    再坚持一点点
    技术到管理的必经之路(2)
    技术到管理的必经之路(1)
    c#进阶(7)—— 异步编程基础(async 和 await 关键字)
    c#进阶(6)—— 网络通信基础知识
  • 原文地址:https://www.cnblogs.com/KardelXiao/p/5550326.html
Copyright © 2020-2023  润新知