一、基本概念
浅拷贝
浅拷贝只是对object
对象指针进行拷贝,不会开辟新的内存。与数据源指向的是同一内存。例如copyA = [A copy]
,capyA
和A
指向的是同一内存,A
的值变化,copyA
的值也会发生变化。
copy
方法做的是浅拷贝。
深拷贝
深拷贝会开辟新的内容,然后对原数据进行复制,新object
对象与数据源指向的是不同的内存,对数据源操作不会影响新object
对象。例如 mutableCopyA = [A mutableCopy]
,mutableCopyA
和A
指向的是两块不同的内存,A的值发生变化不会影响到mutableCopyA
的值。
mutableCopy
方法做的是深拷贝。
二、不同对象类型的深拷贝和浅拷贝
NSString 和 NSMutableString
1、概述
分别创建一个NSString
和NSMutableString
类型字符串,然后用copy
和mutableCopy
,创建新的对象,查看对象指针地址和对象地址的变化。
同时用NSMutableString
类型接收NSString
对象copy
和mutableCopy
之后的值,并操作appendString
,测试是否有问题。
对于NSMutableString
类型copy
和mutableCopy
之后同样操作appendString
,测试是否有问题。
2、示例分析
// 不可变字符串
NSString *str = @"hello World";
NSString *strCopy = [str copy];
NSString *strMutableCopy = [str mutableCopy];
NSLog(@"不可变字符串 copy 和 mutableCopy的区别");
NSLog(@" 对象地址 对象指针地址 ");
NSLog(@" string: %p , %p", str, &str);
NSLog(@" copy: %p , %p", strCopy, &strCopy);
NSLog(@"mutalbeCopy: %p , %p", strMutableCopy, &strMutableCopy);
运行结果
// 可变字符串
NSMutableString *mutableString = [NSMutableString stringWithString:@"hello world"];
NSMutableString *mutableStringCopy = [mutableString copy];
NSMutableString *mutableStringMutableCopy = [mutableString mutableCopy];
NSLog(@"可变字符串 copy 和 mutableCopy的区别");
NSLog(@" 对象地址 对象指针地址 ");
NSLog(@"mutableString: %p , %p", mutableString, &mutableString);
NSLog(@" copy: %p , %p", mutableStringCopy, &mutableStringCopy);
NSLog(@" mutalbeCopy: %p , %p", mutableStringMutableCopy, &mutableStringMutableCopy);
运行结果
分析:
从打印结果来看,不管是copy
还是mutableCopy
,对象的指针地址都发生了变化,说明都会创建新的指针。
针对NSString
类型来看,copy
后对象地址没有变化,说明copy
只是创建了新的指针,指针指向的还是原来的内存地址。mutalbeCopy
之后对象地址也发生了变化,说明mutalbeCopy
创建了新的指针,同时开辟了新的内存并将string
的内容复制了过去。
针对NSMutableString
类型来看,不论是copy
还是mutalbeCopy
,对象的指针地址和对象地址都发生了变化,因此两种形式都开辟了新内存并将内容复制到的新内存中。
PS:
1、使用NSMutableString
类型接收NSString
类型copy
和mutableCopy
之后的值,然后进行appendString
,结果发现copy
之后的对象不能进行appendString
,会发生闪退。
2、使用NSMutableString
类型接收NSMutableString
类型copy
和mutableCopy
之后的值,然后进行appendString
,结果同样发现copy
之后的对象不能进行appendString
,会发生闪退。
3、结论
1、对于不可变类型NSString
来说,copy
是浅拷贝,mutableCopy
是深拷贝,同时mutableCopy
之后变成了NSMutableString
类型。
2、对于可变类型NSMutableString
来说,copy
和mutableCopy
都是深拷贝,都进行了内容拷贝,但是copy
之后数据类型变成了NSString
类型。
3、copy
之后结果是不可变类型,不能直接操作值的改变。mutableCopy
之后结果都是可变数据类型。
NSArray 和 NSMutableArray
1、概述
按照上面NSString
和NSMutableString
系统非集合类对象的思路,对NSArray
和NSMutableArray
系统集合类对象做同样的示例分析。
因为集合类对象里面的元素还可以是对象,因此需要打印内部元素的地址信息,查看是否进行了深拷贝。
2、示例分析
NSArray *cellArray1 = @[@"1", @"2", @"3"];
NSArray *cellArray2 = @[@"4", @"5", @"6"];
NSArray *array = @[cellArray1, cellArray2];
NSArray *arrayCopy = [array copy];
NSArray *arrayMutableCopy = [array mutableCopy];
NSArray *tempArray = array.firstObject;
NSArray *tempArrayCopy = arrayCopy.firstObject;
NSArray *tempArrayMutableCopy = arrayMutableCopy.firstObject;
NSLog(@"不可变数组 copy 和 mutableCopy的区别");
NSLog(@" 对象地址 对象指针地址 firstObject地址 firstObject指针地址");
NSLog(@" array: %p , %p , %p , %p", array, &array, tempArray, &tempArray);
NSLog(@" copy: %p , %p , %p , %p", arrayCopy, &arrayCopy, tempArrayCopy, &tempArrayCopy);
NSLog(@"mutalbeCopy: %p , %p , %p , %p", arrayMutableCopy, &arrayMutableCopy, tempArrayMutableCopy, &tempArrayMutableCopy);
运行结果
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:@[cellArray1, cellArray2]];
NSMutableArray *mutableArrayCopy = [mutableArray copy];
NSMutableArray *mutableArrayMutableCopy = [mutableArray mutableCopy];
NSMutableArray *tempMutableArray = mutableArray.firstObject;
NSMutableArray *tempMutableArrayCopy = mutableArrayCopy.firstObject;
NSMutableArray *tempMutableArrayMutableCopy = mutableArrayMutableCopy.firstObject;
NSLog(@"可变数组 copy 和 mutableCopy的区别");
NSLog(@" 对象地址 对象指针地址 firstObject地址 firstObject指针地址");
NSLog(@"mutableArray: %p , %p , %p , %p", mutableArray, &mutableArray, tempMutableArray, &tempMutableArray);
NSLog(@" copy: %p , %p , %p , %p", mutableArrayCopy, &mutableArrayCopy, tempMutableArrayCopy, &tempMutableArrayCopy);
NSLog(@" mutalbeCopy: %p , %p , %p , %p", mutableArrayMutableCopy, &mutableArrayMutableCopy, tempMutableArrayMutableCopy, &tempMutableArrayMutableCopy);
运行结果
分析:
1、copy
和mutableCopy
之后对象地址的变化与非集合对象变化相同。
2、无论是copy
还是mutableCopy
,内部元素的地址都没有变化,只是指针地址发生了变化。
3、结论
1、从打印的地址结果中可以看出,集合对象和非集合对象表现的结果相同,对于可变对象而言copy
是浅拷贝,mutableCopy
是深拷贝,同时mutableCopy
之后变成了NSMutableArray
类型。
2、对于可变类型NSMutableArray
来说,copy
和mutableCopy
都是深拷贝,都进行了内容拷贝,但是copy
之后数据类型变成了NSArray
类型。
3、值得注意的是,即使mutableCopy
做的也只是浅层深拷贝,即只在新开辟的内存中子项指针指向的还是原来的内存地址。
自定义对象的深拷贝和浅拷贝
1、概述
自定义对象的深拷贝和浅拷贝,首先自定义对象需要实现NSCopying
和NSMutableCopying
协议,否则系统会报错。
下面对自定义对象的深拷贝和浅拷贝方式做代码示例。
2、应用示例
自定义对象Example
的实现代码
@interface Example : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, strong) NSString *exampleTitle;
@end
@implementation Example
@synthesize exampleTitle;
// 实现copyWithZone方法
- (id)copyWithZone:(NSZone *)zone {
Example *example = [[self class] allocWithZone:zone];
example.exampleTitle = self.exampleTitle;
return example;
}
// 实现mutableCopyWithZone方法
- (id)mutableCopyWithZone:(NSZone *)zone {
Example *example = [[self class] allocWithZone:zone];
example.exampleTitle = self.exampleTitle;
return example;
}
@end
自定义对象的copy和mutableCopy
Example *example1 = [[Example alloc] init];
example1.exampleTitle = @"test1";
Example *exampleCopy = [example1 copy];
Example *exampleMutableCopy = [example1 mutableCopy];
NSString *title = example1.exampleTitle;
NSString *titleCopy = exampleCopy.exampleTitle;
NSString *titleMutableCopy = exampleMutableCopy.exampleTitle;
NSLog(@"自定义对象 copy 和 mutableCopy的区别");
NSLog(@" 对象地址 对象指针地址 属性地址 属性指针地址");
NSLog(@" Example: %p , %p , %p , %p", example1, &example1, title, &title);
NSLog(@" copy: %p , %p , %p , %p", exampleCopy, &exampleCopy, titleCopy, &titleCopy);
NSLog(@"mutalbeCopy: %p , %p , %p , %p", exampleMutableCopy, &exampleMutableCopy, titleMutableCopy, &titleMutableCopy);
运行结果
小结:
1、自定义对象使用copy
和mutableCopy
时,需要实现NSCopying
和NSMutableCopying
协议,否则代码运行时会报错。
2、自定义对象使用copy
或mutableCopy
之后,对象的地址和指针地址都发生了变化,但是属性地址还是指向原来的地址,也就是说实际也是浅层的深拷贝。
3、因为copyWithZone
方法和mutableCopyWithZone
都是自己重写的,所以其实自定义对象使用copy
还是mutableCopy
并没有什么区别。
数组中的元素是自定义对象时的深拷贝和浅拷贝
上代码
Example *example2 = [[Example alloc] init];
example2.exampleTitle = @"test2";
NSArray *exampleArray = [[NSArray alloc] initWithObjects:example2, nil];
NSArray *shallowCopyArray = [[NSArray alloc] initWithArray:exampleArray];
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:exampleArray copyItems:YES];
Example *shallowCopyExample = shallowCopyArray.firstObject;
Example *deepCopyExample = deepCopyArray.firstObject;
example2.exampleTitle = @"where is test2";
NSLog(@"数组中存在自定义对象的浅拷贝和深拷贝");
NSLog(@" 对象地址 对象指针地址 属性地址 属性指针地址 属性值");
NSLog(@" exampleArray: %p , %p , %p , %p , %@", exampleArray, &exampleArray, example2, &example2, example2.exampleTitle);
NSLog(@"shallowCopyArray: %p , %p , %p , %p , %@", shallowCopyArray, &shallowCopyArray, shallowCopyExample, &shallowCopyExample, shallowCopyExample.exampleTitle);
NSLog(@" deepCopyArray: %p , %p , %p , %p , %@", deepCopyArray, &deepCopyArray, deepCopyExample, &deepCopyExample, deepCopyExample.exampleTitle);
运行结果
小结:
1、从运行结果中可以看出,使用initWithArray:
方法实现的是浅拷贝,原对象的值发生改变新数组中也会同步改变。
2、使用initWithArray: copyItems:
方法实现的是深拷贝,数组和数组中的对象都会开辟新的内存,因此原数组中的数据发生变化,新数组不会变化。