学习资料
首先iOS拷贝可以区分为 深拷贝
和 浅拷贝
。
浅拷贝
:拷贝了对象的指针,可以理解为给一条狗加了一条绳子。
深拷贝
:直接拷贝对象到另外一块内存地址中,可以理解为新买了一条狗。
- 深浅拷贝取决于拷贝后的对象是不是和被拷贝对象的地址相同,如果不同,则产生了新的对象,即为深拷贝。如果相同,则只是指针拷贝,相当于retain一次原对象,即为浅拷贝。
-
copy
和mutableCopy
和是不是深拷贝没有关系,我们判断是不是深拷贝,仅仅是判断拷贝之后的对象和原对象是不是内存地址相同。不相同的话就是深拷贝,相同就是浅拷贝。
为什么定义字符串属性的时候要用 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
提供了copy
和mutableCopy
方法,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
方法时,系统会把copy
和mutableCopy
自动替换为copyWithZone:
和mutableCopyWithZone:
。即copy
和mutableCopy
只是简便方法。如果集合内元素遵守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;
}