前言
在开发的时候,使用copy的频率还是挺高的,我们只要copy定义的属性的设置方法并不保留新值,只是其拷贝一份值,为什么NSString、NSArray、NSDictionary属性的定义说那个copy,如果使用strong关键字有什么问题?所以这节就讲一下以及什么使用深拷贝和浅拷贝的问题。
概念
- 浅拷贝:浅拷贝就是指针拷贝,就是拷贝一份指向该对象的指针,就是复制的对象和原对象都指向同一个地址
- 深拷贝:深拷贝是内容拷贝,真正的复制一份,复制对象的内容。复制的对象指向新的地址。如下图:
copy和mutablecopy概念
copy:copy拷贝出来的对象类型总是不可变类型(例如, NSString, NSDictionary, NSArray等等)
mutableCopy拷贝出来的对象类型总是可变类型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)
对象要想具有copy和mutablecopy功能要是NSCopying和NSMutableCopy协议,实现copywithzone和mutablecopywithzone
非集合类对象的copy与mutableCopy
系统非集合类对象指的是 NSString, NSNumber ... 之类的对象。下面先看个非集合类immutable对象拷贝的例子
NSString *string = @"origin";
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
通过查看内存,可以看到 stringCopy 和 string 的地址是一样,进行了指针拷贝;而 stringMCopy 的地址和 string 不一样,进行了内容拷贝;
再看mutable对象拷贝例子:
NSMutableString *string = [NSMutableString stringWithString: @"origin"];
//copy
NSString *stringCopy = [string copy];
NSMutableString *mStringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
//change value
[mStringCopy appendString:@"mm"]; //crash
[string appendString:@" origion!"];
[stringMCopy appendString:@"!!"];
运行以上代码,会在第7行crash,原因就是 copy 返回的对象是 immutable 对象。注释第7行后再运行,查看内存,发现 string、stringCopy、mStringCopy、stringMCopy 四个对象的内存地址都不一样,说明此时都是做内容拷贝.
在非集合类对象中得出结论:
- 对immutable对象进行copy操作,是指针复制mutableCopy操作时内容复制
- 对mutable对象进行copy和mutableCopy都是内容复制
- 可变对象通过copy之后就变成不可变了对象。
集合类对象的copy与mutableCopy
-
对不可变集合类:
- 不可变集合类对象是指NSArray、NSDictionary、NSSet ... 之类的对象。下面先看集合类immutable对象使用copy和mutableCopy的一个例子:
NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]; NSArray *copyArray = [array copy]; NSMutableArray *mCopyArray = [array mutableCopy]; [mCopyArray addobject: @"e"];
查看内容,可以看到copyArray和array的地址是一样的,而mCopyArray和array的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。mCopyArray可以添加对象,是可变对象
- 强调:此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝。
-
对可变集合类:
- 可变集合类对象是指NSMutableArray、NSMutableDictionary、NSMutableSet ... 之类的对象。下面先看集合类immutable对象使用copy和mutableCopy的一个例子
NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil]; NSArray *copyArray = [array copy]; NSMutableArray *m1CopyArray = [array copy]; NSMutableArray *mCopyArray = [array mutableCopy]; [mCopyArray addObject:@"d"]; [m1CopyArray addObject:@"d"]; //crash
查看内存,
(lldb) po array
<__NSArrayM 0x600001963540>(
a,
b,
c
)
(lldb) po copyArray
<__NSArrayI 0x6000019635d0>(
a,
b,
c
)
(lldb) po mCopyArray
<__NSArrayM 0x600001963390>(
a,
b,
c,
d
)
(lldb) po m1CopyArray
<__NSArrayI 0x600001963570>(
a,
b,
c
)
```
打印的地址可以得出结论:
* 在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制。
* 在集合类对象中,对mutable对象进行copy和mutableCopy都是内容复制。
* 在集合类对象中,对对象进行copy的对象就是不可变的,进行mutable就是可变的
为啥对NSString、NSArray、NSDictionary进行copy,如果进行strong处理有什么后果
copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。例如:
//定义一个以 strong 修饰的 array:
@property (nonatomic ,readwrite, strong) NSArray *array;
进行如下操作
NSArray *array = @[ @1, @2, @3, @4 ];
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];
self.array = mutableArray;
[mutableArray removeAllObjects];;
NSLog(@"self.array:%@",self.array);
[mutableArray addObjectsFromArray:array];
self.array = [mutableArray copy];
[mutableArray removeAllObjects];;
NSLog(@"self.array:%@",self.array);
打印结果如下所示:
self.array:(
),
self.array:(
1,
2,
3,
4
)
结果说明如果使用strong来定义不可变对象,它的子类可变对象,有可能该对象的指针指向他的子类,他的子类改变了,该对象也就改变了。这样就不经意篡改了该对象的值,不安全了。
总结
通过本章了解,可以大概总结一下几点:
对于不可变对象(不可变对象和不可变集合对象),进行copy都是指针拷贝,进行mutablecopy都是内容拷贝。
对于可变对象(可变对象和可变集合对象),进行copy和mutablecopy都是内容copy。
不管是可变对象和不可变对象进行copy操作,产生的都是不可变的对象,进行mutablecopy操作产生的都是可变的对象。
对任何一个对象进行深拷贝,都是单层深拷贝。例如:对NSArray进行深拷贝,但是数组里面的元素还是指针拷贝。就是两个数组的地址不一样,但是里面的元素地址是一样的。所以要想让数组中的元素也是不一样的地址,那就对每个元素进行深拷贝。