iOS 谈谈copy和mutableCopy

copy 是浅拷贝,mutableCopy 是深拷贝,从我学习的时候开始 老师貌似也是这么说的,我也一直是这么记的【尴尬】这是不对的!!!!!!!!!!

创建一个对象, 该对象与源对象的内容一致,此时使用copy与mutableCopy的区别:
copy拷贝出来的对象类型总是不可变类型(例如, NSString, NSDictionary, NSArray等等)
mutableCopy拷贝出来的对象类型总是可变类型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)

声明属性的时候修饰使用copy修饰的原因:
补充一点堆栈的介绍:
堆和栈是操作系统的内存中堆和栈,不是数据结构中的堆和栈。
1、堆(heap)区:堆是由程序员分配和释放,用于存放进程运行中被动态分配的内存段,它大小并不固定,可动态扩张或缩减。当进程调用alloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用realse释放内存时,被释放的内存从堆中被剔除(堆被缩减),因为我们现在iOS基本都使用ARC来管理对象,所以不用我们程序员来管理,但是我们要知道这个对象存储的位置。
2、栈(stack)区:栈是由编译器自动分配并释放,用户存放程序临时创建的局部变量,存放函数的参数值,局部变量等。也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味这在数据段中存放变量)。除此以外在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也回被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上将我们可以把栈看成一个临时数据寄存、交换的内存区。

  • block为什么要用copy?
    MRC下不使用copy修饰, block存储在栈区, 作用域结束, block这个局部变量被销毁, 而一个strong指针还指向了这个被回收的地址.所以使用copy,把block拷贝到堆区, 并且指向堆区的block. 但是在ARC下无论copy,还是stong都无所谓, 因为系统已经在创建block的时候, 已经拷贝到堆区了。copy在MRC中是为了保护block的封装性将其移动至堆区。但在ARC中更多的是为了语义化、因为系统自动帮你移动了

补充个常用的NSString用copy修饰的原因:
用copy是为了安全,防止NSMutableString赋值给NSString时,前者修改引起后者值变化而用的.这个也适用于别的下文也会提到并验证。

@property (nonatomic,strong) NSString *myName;
NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"daLi"];
    self.myName = mStr;
    NSLog(@"%@", self.myName);
    [mStr appendString:@"好美"];
    NSLog(@"%@", mStr);
    NSLog(@"%@", self.myName);
    /*
     分析:self.myName声明的时候使用strong修饰,会跟着mStr的改变而改变
     输出:
     2017-12-11 16:41:39.146126+0800 Demo[20438:1163039] daLi
     2017-12-11 16:41:39.146357+0800 Demo[20438:1163039] daLi好美
     2017-12-11 16:41:39.146496+0800 Demo[20438:1163039] daLi好美
     */
@property (nonatomic,copy) NSString *myName;
NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"daLi"];
    self.myName = mStr;
    NSLog(@"%@", self.myName);
    [mStr appendString:@"好美"];
    NSLog(@"%@", mStr);
    NSLog(@"%@", self.myName);
    /*
     分析:self.myName声明的时候使用copy修饰,不会跟着mStr的改变而改变
     输出:
     2017-12-11 16:46:08.367018+0800 Demo[20507:1166962] daLi
     2017-12-11 16:46:08.367227+0800 Demo[20507:1166962] daLi好美
     2017-12-11 16:46:08.367353+0800 Demo[20507:1166962] daLi
     */

浅拷贝和深拷贝的分析
先介绍一下什么是iOS里面的系统容器类:
比如NSArray,NSMutableArray,NSDictionary,NSMutableDictionary....
浅拷贝和深拷贝?????英文就是shallow copy和deep copy

  • 浅拷贝:正常的拷贝,生成一个新的容器,但却是和原来的容器共用内部的元素;
  • 深拷贝:不仅生成新的容器,还生成了新的内部元素。

稍作总结:浅拷贝复制容器,深拷贝复制容器及其内部元素
(copy 与 mutableCopy 不等同于浅拷贝与深拷贝)
接下来用代码验证这些:

NSMutableArray *mArr = [NSMutableArray arrayWithObject:@1];
    NSLog(@"1-%@--%p", mArr, mArr);
    NSMutableArray *marr0 = [NSMutableArray arrayWithObject:mArr];
    NSLog(@"2-%@--%p--%p", marr0, marr0, marr0[0]);
    NSMutableArray *mCopyArr = [marr0 mutableCopy];//替换成copy是一样的效果
    NSLog(@"3-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
    [mCopyArr[0] addObject:@2];
    NSLog(@"4-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
    NSLog(@"5-%@--%p--%p", marr0, marr0, marr0[0]);
    [marr0[0] addObject:@3];
    NSLog(@"6-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
    NSLog(@"7-%@--%p--%p", marr0, marr0, marr0[0]);
/*
 修改了mCopyArr的元素,marr0也跟着改变了元素,因为二者的元素是共用的,[marr0 mutableCopy]只拷贝了数组容器,里面的元素并没有拷贝,而是和marr0共用的元素,所以marr0[0]、mCopyArr[0]元素的地址是一个,无论修改它们俩哪一个,对应的数组marr0、mCopyArr里面的元素都会跟着改变。
 由此验证出来:容器类的mutableCopy和copy是浅拷贝,只拷贝容器,并不拷贝元素!!
 输出结果:
 2017-12-11 14:41:53.551658+0800 Demo[19619:1091522] 1-(
 1
 )--0x600000059f50
 2017-12-11 14:41:53.551876+0800 Demo[19619:1091522] 2-(
 (
 1
 )
 )--0x6000002576a0--0x600000059f50
 2017-12-11 14:41:53.552080+0800 Demo[19619:1091522] 3-(
 (
 1
 )
 )--0x60000000b9f0--0x600000059f50
 2017-12-11 14:41:53.552357+0800 Demo[19619:1091522] 4-(
 (
 1,
 2
 )
 )--0x60000000b9f0--0x600000059f50
 2017-12-11 14:41:53.552545+0800 Demo[19619:1091522] 5-(
 (
 1,
 2
 )
 )--0x6000002576a0--0x600000059f50
 2017-12-11 14:41:53.552687+0800 Demo[19619:1091522] 6-(
 (
 1,
 2,
 3
 )
 )--0x60000000b9f0--0x600000059f50
 2017-12-11 14:41:53.552806+0800 Demo[19619:1091522] 7-(
 (
 1,
 2,
 3
 )
 )--0x6000002576a0--0x600000059f50
 */

写一个NSArray验证一下

NSArray *arr = @[@"1"];
    NSLog(@"1-%@--%p", arr, arr);
    NSArray *arr0 = arr;
    NSLog(@"2-%@--%p", arr0, arr0);
    NSArray *arr1 = [arr copy];
    NSLog(@"3-%@--%p", arr1, arr1);
    NSArray *arr2 = [arr mutableCopy];
    NSLog(@"4-%@--%p", arr2, arr2);
    arr = @[@"2"];
    NSLog(@"5-%@--%p", arr, arr);
    NSLog(@"6-%@--%p", arr0, arr0);
    NSLog(@"7-%@--%p", arr1, arr1);
    NSLog(@"8-%@--%p", arr2, arr2);
    /*
     分析:
     arr初始化指向的是@[@"1"]数组,
     1、arr0是直接用arr赋值的,都是指向了@[@"1"],当arr = @[@"2"],arr指向了@[@"2"]这个数组,而arr0不变,因为arr0依旧指向的是@[@"1"];
     2、arr1,[arr copy],使得arr1指向了arr指向的@[@"1"],后面arr的指向改变并不会导致arr1的指向改变;
     3、arr2,[arr mutableCopy],使得arr1指向了arr指向的@[@"1"],后面arr的指向改变并不会导致arr1的指向改变;
     输出:
     2017-12-11 15:12:21.130514+0800 Demo[19697:1107933] 1-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.130690+0800 Demo[19697:1107933] 2-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.130835+0800 Demo[19697:1107933] 3-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.130994+0800 Demo[19697:1107933] 4-(
     1
     )--0x604000056770
     2017-12-11 15:12:21.131122+0800 Demo[19697:1107933] 5-(
     2
     )--0x604000002380
     2017-12-11 15:12:21.131255+0800 Demo[19697:1107933] 6-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.131342+0800 Demo[19697:1107933] 7-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.131431+0800 Demo[19697:1107933] 8-(
     1
     )--0x604000056770
     */

cpoy 与直接赋值的对比分析

直接赋值:
    NSMutableArray *mArr = [NSMutableArray arrayWithObjects:@"1", nil];
    NSLog(@"0-%@--%p", mArr, mArr);
    NSArray *arr = mArr;
    NSLog(@"1-%@--%p", arr, arr);
    [mArr addObject:@"2"];
    NSLog(@"2-%@--%p", mArr, mArr);
    NSLog(@"3-%@--%p", arr, arr);
/*
 分析:使用直接赋值的方式给arr=mArr,可变数组mArr添加对象是在赋值之后, 为什么后面添加对象还会影响到不可变数组arr呢?? 原因很简单, 因为Objective-C支持多态. 所以表面上arr是NSArray对象, 其实骨子里是NSMutableArray对象.这样的话将会对后期DEBUG增加很大的成本, 可能会导致莫名其妙的错误.开发过程中应该注意此类情况
 输出:
 2017-12-11 15:46:21.532252+0800 Demo[20106:1129830] 0-(
 1
 )--0x604000441350
 2017-12-11 15:46:21.532407+0800 Demo[20106:1129830] 1-(
 1
 )--0x604000441350
 2017-12-11 15:46:21.532528+0800 Demo[20106:1129830] 2-(
 1,
 2
 )--0x604000441350
 2017-12-11 15:46:21.532652+0800 Demo[20106:1129830] 3-(
 1,
 2
 )--0x604000441350
 */
copy/mutableCopy:
NSMutableArray *mArr = [NSMutableArray arrayWithObjects:@"1", nil];
    NSLog(@"0-%@--%p", mArr, mArr);
    NSArray *arr = [mArr copy];//mutableCopy
    NSLog(@"1-%@--%p", arr, arr);
    [mArr addObject:@"2"];
    NSLog(@"2-%@--%p", mArr, mArr);
    NSLog(@"3-%@--%p", arr, arr);
/*
 分析:arr使用copy获取之后,再次修改mArr不会造成arr的改变,保障了arr的安全。
 输出:
 2017-12-11 15:51:29.749360+0800 Demo[20137:1133165] 0-(
 1
 )--0x60000005daf0
 2017-12-11 15:51:29.749537+0800 Demo[20137:1133165] 1-(
 1
 )--0x600000007ec0
 2017-12-11 15:51:29.749639+0800 Demo[20137:1133165] 2-(
 1,
 2
 )--0x60000005daf0
 2017-12-11 15:51:29.749787+0800 Demo[20137:1133165] 3-(
 1
 )--0x600000007ec0
 */

然后再写一个字典的来验证验证:

NSDictionary *dictA = @{@"a": @"a"};
    NSDictionary *dictB = dictA;
    dictA = @{@"b": @"b"};
    NSLog(@"%@ - %p",dictA, dictA); // {b = b}
    NSLog(@"%@ - %p",dictB, dictB); // {a = a}
    
    /*
     dictB = dictA, 代表dictA和dictB都指向{@"a" : @"a"},
     dictA = @{@"b" : @"b"}这行代码之后dictA就转而指向@{@"b" : @"b"}了.
     而dictA仍然指向{@"a" : @"a"}.
     所以输出:
     {
     b = b;
     } - 0x604000039ae0
     {
     a = a;
     } - 0x604000030180
     */
    
    Person *pA = [[Person alloc] init];
    pA.name = @"A1";
    Person *pB = [[Person alloc] init];
    pB = pA;
    pA.name = @"A2";
    NSLog(@"%@ - %p - %p", pA.name, pA, &pA);
    NSLog(@"%@ - %p - %p", pB.name, pB, &pB);
    /*
      Person *pB = [[Person alloc] init];这句代码是没有任何作用的, 对pB后面的指向没有任何影响, pB = pA这句代码过后, pB和pA指向的是同一个对象, 那么通过pA修改其指向的对象的name属性, 自然也会影响到pB指向的对象的name属性. 因为他们两个指向的是同一个对象
     A2 - 0x600000017a50 - 0x7fff5c47cb40
     A2 - 0x600000017a50 - 0x7fff5c47cb38
     */

你可能感兴趣的:(iOS 谈谈copy和mutableCopy)