iOS基础 - copy 和 mutableCopy

在iOS中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying/NSMutableCopying 协议的类可以发送copy/mutableCopy消息,否则就会发生异常。默认的iOS类并没有遵守这两个协议。如果想自定义一下copy/mutableCopy 那么就必须遵守NSCopying/NSMutableCopying,并且实现 copyWithZone: /mutableCopyWithZone:方法.

copy 和 mutableCopy分两种情况讨论,非容器类对象和容器类对象。需要有一个注意的点是,copy返回的对象是不可变的。

一 、对于非容器类对象:

这里指的是NSString,NSNumber等等一类的对象

  • retain:对象引用计数器自增+1,与原对象指向同一地址。结果和浅拷贝一样,
  • copy:
    • 对不可变对象 执行浅拷贝,等同与retain,指针拷贝,引用计数器+1(同上);
    • 对可变对象 执行深拷贝,等同mutableCopy,对象拷贝(同下)。
  • mutableCopy:
    • 对不可变对象 执行深拷贝,创建新的实例对象,分配了一块新的内存,拷贝原对象的值。
    • 对可变对象 执行深拷贝,创建新的实例对象,分配了一块新的内存,拷贝原对象的值。
实际验证一下,结果显而易见:
    
    //对非容器不可变对象copy
    NSString * str      = @"a";
    NSString * strCopy  = [str copy];
    NSLog(@"对非容器不可变对象copy");
    NSLog(@"str     %p",str);
    NSLog(@"strCopy %p",strCopy);
    
    //对非容器可变对象copy
    NSMutableString * mstr = [[NSMutableString alloc] init];
    mstr.string = @"b";
    NSMutableString * mstrCopy = [mstr copy];
    NSLog(@"对非容器可变对象copy");
    NSLog(@"mstr     %p",mstr);
    NSLog(@"mstrCopy %p",mstrCopy);
    
    //对非容器不可变对象mutablecopy
    NSString * strMutableCoy  = [str mutableCopy];
    NSLog(@"对非容器不可变对象mutablecopy");
    NSLog(@"str             %p",str);
    NSLog(@"strMutableCoy   %p",strMutableCoy);
    
    //对非容器可变对象mutablecopy
    NSMutableString * mstrMutableCopy = [mstr mutableCopy];
    NSLog(@"对非容器可变对象mutableCopy");
    NSLog(@"mstr            %p",mstr);
    NSLog(@"mstrMutableCopy %p",mstrMutableCopy);

iOS基础 - copy 和 mutableCopy_第1张图片
非容器对象copy:mutablecopy.png

综上,对于非容器类对象,我们可以认为 :

  • 不可变对象复制,copy是浅拷贝(指针复制)和mutableCopy就是深拷贝(对象复制)。
  • 对可变对象复制,都是深拷贝。

二 、对于容器类对象:

这里指的是NSArray,NSDictionary等。对于容器类对象本身,上面的结论同样适用。
需要探讨的是复制后 容器内元素对象 的变化。

    //对容器 不可变对象 copy
    NSArray * arr      = [NSArray arrayWithObjects:@"a",@"b", nil];
    NSArray * arrCopy  = [arr copy];
    NSLog(@"对容器不可变对象copy");
    NSLog(@"arrarr.lastObject  %p",arr.lastObject);
    NSLog(@"arrCopy.lastObject %p",arrCopy.lastObject);
    
    //对容器 可变对象 copy
    NSMutableArray * mArr = [[NSMutableArray alloc] initWithObjects:@"a",@"b", nil];
    NSMutableArray * mArrCopy = [mArr copy];
    NSLog(@"对容器可变对象copy");
    NSLog(@"mArr.lastObject    %p",mArr.lastObject);
    NSLog(@"mArrCopy.lastObject%p",mArrCopy.lastObject);
    
    //对容器 不可变对象 mutablecopy
    NSArray * arrMutableCopy  = [arr mutableCopy];
    NSLog(@"对非容器不可变对象mutablecopy");
    NSLog(@"arr.lastObject              %p",arr.lastObject);
    NSLog(@"arrMutableCopy.lastObject   %p",arrMutableCopy.lastObject);
    
    //对容器 可变对象 mutablecopy
    NSMutableArray * mArrMutableCopy = [mArr mutableCopy];
    NSLog(@"对非容器可变对象mutableCopy");
    NSLog(@"mArr.lastObject             %p",mArr.lastObject);
    NSLog(@"mArrMutableCopy.lastObject  %p",mArrMutableCopy.lastObject);
iOS基础 - copy 和 mutableCopy_第2张图片
容器对象copy:mutablecopy.png

综上,对于容器类对象,我们可以认为 :

  • 对于容器类对象本身,copy/mutablecopy 执行结果同非容器类对象。
  • 对于容器类对象 容器内的元素对象,都是浅拷贝。

如何实现容器内元素对象的深拷贝

两种方法

    NSArray * arr = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"x"],@"a",@"b",[NSMutableString stringWithString:@"c"], nil];
    NSArray * arrDeepCopy=[[NSArray alloc] initWithArray: arr copyItems: YES];
    NSArray * arrTrueDeepCopy = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: arr]];
    
    NSLog(@"%p  %p  %p",arr.firstObject,            [arr objectAtIndex:1],              arr.lastObject);
    NSLog(@"%p  %p  %p",arrDeepCopy.firstObject,    [arrDeepCopy objectAtIndex:1],      arrDeepCopy.lastObject);
    NSLog(@"%p  %p  %p",arrTrueDeepCopy.firstObject,[arrTrueDeepCopy objectAtIndex:1],  arrTrueDeepCopy.lastObject);

元素对象深拷贝.jpg

综上可见:

  • arrTrueDeepCopy 使用 归档解档操作 是完全意义上的深拷贝,拷贝了元素对象本身;
  • arrDeepCopy 使用 copyItems 是官方文档定义的深拷贝方法。其实不是,arrDeepCopy内的不可变元素其实还是指针复制。原因如下。

因为如果容器的某一元素是不可变的,复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。

举个例子,[[arr objectAtIndex:0]appendstring:@”123”] 后其他的容器内对象并不会受影响(深拷贝的)。[[arr objectAtIndex:1] 和 [[arrDeepCopy objectAtIndex:1] 尽管是指向同一块内存地址,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝,但是apple的官方文档将其列为deep copy了,并添加了copy和mutablity的关系说明,故在此做一说明。

receiver是不可变对象的时候,接收到copy消息时。只要达到retain的效果就可以了。这样一方面可以满足定义的要求,另一主面可以节省资源。这也是为什么copy和mutablecopy会有这样的实现。

具体应用

在修饰不可变的对象时,例如NSString 和NSArray 等等时,推荐使用copy 而非其他修饰,更有安全性。

相关参考链接 c杂谈之指针与数组

你可能感兴趣的:(iOS基础 - copy 和 mutableCopy)