iOS 拷贝

学习资料
首先iOS拷贝可以区分为 深拷贝浅拷贝
浅拷贝:拷贝了对象的指针,可以理解为给一条狗加了一条绳子。
深拷贝 :直接拷贝对象到另外一块内存地址中,可以理解为新买了一条狗。

  • 深浅拷贝取决于拷贝后的对象是不是和被拷贝对象的地址相同,如果不同,则产生了新的对象,即为深拷贝。如果相同,则只是指针拷贝,相当于retain一次原对象,即为浅拷贝。
  • copymutableCopy和是不是深拷贝没有关系,我们判断是不是深拷贝,仅仅是判断拷贝之后的对象和原对象是不是内存地址相同。不相同的话就是深拷贝,相同就是浅拷贝。

为什么定义字符串属性的时候要用 copy

一个不可变字符串和一个可变字符串指向同一块地址的时候,修改可变字符串,不可变字符串也跟着变化了。

NSString *name1 = @"";
// self.name1 == , 0x100a34118
NSLog(@"self.name1 == %@, %p",name1, name1); 

NSMutableString *strMu = @"dd".mutableCopy;
// mutable == dd, 0x281f09860
NSLog(@"mutable == %@, %p",strMu, strMu);

name1 = strMu;
// self.name2 == dd, 0x281f09860
NSLog(@"self.name2 == %@, %p",name1, name1);

[strMu appendString:@"mm"];
// self.name3 == ddmm, 0x281f09860
NSLog(@"self.name3 == %@, %p",name1, name1);    

NSObject提供了copymutableCopy 方法,copy复制后对象是不可变对象(immutable),mutableCopy复制后的对象是可变对象(mutable),与原始对象是否可变无关。

对非集合类对象 copy 和 mutableCopy

  • 对不可变对象执行copy操作,是指针复制<浅拷贝>,执行mutableCopy操作是内容复制<深拷贝>。
  • 对可变对象执行copy操作和mutableCopy操作都是内容复制<深拷贝>。

[immutableObject copy]; // 浅复制
[immutableObject mutableCopy]; // 深复制
[mutableObject copy]; // 深复制
[mutableObject mutableCopy]; // 深复制

集合类对象的 copy 和 mutableCopy

  • 对于集合类对象的拷贝,我们探讨的是内部元素对应的地址是不是变化,而不是集合本身的地址是否变化了。
集合浅拷贝
NSMutableString *red = @"red".mutableCopy;
NSMutableString *green = @"green".mutableCopy;
NSMutableString *blue = @"blue".mutableCopy;

NSArray *arrOrigin = @[red, green, blue];
NSMutableArray *arrMu1 = arrOrigin.mutableCopy;
NSLog(@"origin == %p",arrOrigin);
NSLog(@"mut == %p",arrMu1);

NSMutableString *strMu = arrMu1.firstObject;
NSLog(@"red == %p",red);
NSLog(@"strMu == %p",strMu);

打印结果为:
originArr == 0x28297bcc0
mutArr == 0x28297ba80

red == 0x28297b1b0
strMu == 0x28297b1b0

虽然集合的地址变化了,但是内部元素的地址没有变,所以是浅拷贝。

集合深拷贝

有两种方式对容器类对象进行深复制:

  • 第一种方法是:使用initWithArray: copyItems:类型方法,其中,第二个参数为YES。
  • 第二种方法是:使用归档、解档。

方式一:initWithArray: copyItems:
下面先看如何使用initWithArray: copyItems:类型方法。使用该方法进行深复制时,第二个参数为YES。如果使用该方法对集合进行深复制,那么集合内每个元素都会收到copyWithZone:消息,我们平常使用copy、mutableCopy方法时,系统会把copymutableCopy自动替换为copyWithZone:mutableCopyWithZone:。即copymutableCopy只是简便方法。如果集合内元素遵守NSCopying协议,元素被复制到新的集合。如果集合内元素不遵守NSCopying协议,用这样的方式进行深复制,会在运行时产生错误。

copyWithZone:产生的是浅复制,所以,这种方法只能产生一层深复制 one-level-deep copy,如果集合内元素仍然是集合,则子集合内元素不会被深复制,只对子集合内元素指针进行复制。

如果集合内元素为不可变对象,发送copyWithZone:消息后进行指针复制,该对象仍然不可变,因此只进行了指针复制。
如果集合内元素为可变对象,发送copyWithZone:消息后进行的是内容复制,复制后该元素不可变,此时,完成了一层深复制。

NSMutableString *red = @"red".mutableCopy;
NSMutableString *green = @"green".mutableCopy;
NSString *blue = @"blue";

NSArray *arrOrigin = @[red, green, blue];
NSMutableArray *arrMu1 = arrOrigin.mutableCopy;
NSMutableArray *arrDeep = [NSMutableArray.alloc initWithArray:arrOrigin copyItems:true];

NSMutableString *strMuFirst = arrMu1.firstObject;
NSMutableString *strDeepFirst = arrDeep.firstObject;
NSLog(@"red == %p",red);
NSLog(@"strMuFirst == %p",strMuFirst);
NSLog(@"strDeepFirst == %p",strDeepFirst);
NSLog(@"\n");
NSMutableString *strMu = arrMu1.lastObject;
NSMutableString *strDeep = arrDeep.lastObject;
NSLog(@"blue == %p",blue);
NSLog(@"strMu == %p",strMu);
NSLog(@"strDeep == %p",strDeep);

/// 此时元素是可变元素,此时进行了深拷贝
red == 0x2821c6cd0
strMuFirst == 0x2821c6cd0
strDeepFirst == 0xf3a8558d022e8484
 
/// 这个元素是不可变元素,此时进行了浅拷贝
blue == 0x100634288
strMu == 0x100634288
strDeep == 0x100634288

方式二 归档、解档

// 5.使用归档进行完全深复制。
- (void)trueDeepCopy {
    // 1.创建一个可变数组,数组第一个元素是另一个可变数组,第二个元素是另一个不可变数组。
    NSMutableString *hue = [NSMutableString stringWithString:@"hue"];
    NSMutableString *saturation = [NSMutableString stringWithString:@"saturation"];
    NSMutableString *brightness = [NSMutableString stringWithString:@"brightness"];
    NSMutableArray *hsbArray1 = [NSMutableArray arrayWithObjects:hue, saturation, brightness, nil];
    NSArray *hsbArray2 = [NSArray arrayWithObjects:hue, saturation, brightness, nil];
    NSMutableArray *hsbArray3 = [NSMutableArray arrayWithObjects:hsbArray1, hsbArray2, nil];
    
    // 2.通过归档、解档进行完全深复制。
    NSData *dataArea = [NSKeyedArchiver archivedDataWithRootObject:hsbArray3];
    NSMutableArray *hsbArray4 = [NSKeyedUnarchiver unarchiveObjectWithData:dataArea];
    
    // 3.输出hsbArray3和hsbArray4数组第一个元素内存地址。
    NSLog(@"Memory location of \n hsbArray3.firstObject = %p, \n hsbArray4.firstObject = %p",hsbArray3.firstObject, hsbArray4.firstObject);
打印结果:
Memory location of 
 hsbArray3.firstObject = 0x60000004b100, 
 hsbArray4.firstObject = 0x60000004b1f0
}

内存地址不一样,进行了深拷贝。

自定义对象的拷贝
自定义对象的拷贝,需要实现NSCoping或者NSMutableCoping协议。

@interface Person()
@end

@implementation Person

-(id)copyWithZone:(NSZone *)zone{
    Person *p = [Person.alloc init];
    p.name = self.name;
    return p;
}

你可能感兴趣的:(iOS 拷贝)