深拷贝和浅拷贝

深拷贝和浅拷贝这个问题说简单也简单,说复杂也复杂。

简单的说:浅复制就是指针拷贝;深复制就是内容拷贝。

非容器

NSString,NSNumber这些不能包含其他对象的叫做非容器类。

系统类

NSString与NSMutableString
可变与不可变的区别在与内存管理方式上,NSMutableString会管理一块内存用于存放数据(在这里是字符),比如上面我们分配了1024个字符,那么当前数据如果没超过1024个字符限制的话,NSMutableString 直接往里填就可以了,如果超出,那么会像NSString一样,重新开辟一段更大的内存,然后把原来的字符逐个拷贝到新的地址,但相对来说,内存分配的次数大大减少了,性能也就提高了。

当你需要频繁变动字符串的时候,NSMutableString可以极大的提高性能,但是需要耗费更大的内存,造成浪费,所以在实际编程的时候还是需要取舍到底哪个类比较适合当前业务。

对于NSString,创建方式并不影响拷贝方式。copy是浅拷贝,mutablecopy是深拷贝。
对于NSMutableString,右侧无论是copy还是mutableCopy,都会创建一个新对象,都是属于深拷贝。

自定义类

其实在iOS中并不是所有的对象都支持copy、mutableCopy方法,只有遵守NSCopying协议的类可以发送copy消息,遵守NSMutableCopying协议的类才可以发送mutableCopy消息,否则就会崩溃。我们可以非常方便的使用系统类中的copy,mutableCopy方法,是因为这些系统类本身就实现了NSCopying,NSMutableCopy协议,大家可以进入接口文档进行查看。

自定义类无论是用copy还是mutableCopy,复制后都创建了一个新的对象。都是深拷贝。

对于自定义类对象中的属性,是深拷贝还是浅拷贝?

@property (copy, nonatomic) NSString *optionTitle;
若是NSString的话。
person.name = [otherName copy]; 和 person.name = otherName;都是浅拷贝
person.name = [otherName mutableCopy]; 是深拷贝。

@property (copy, nonatomic) NSMutableString *optionTitle;
若是NSMutableString取决于修饰属性是copy还是strong?
copy,所以创建了新的字符串,属于深拷贝;
strong会持有原来的对象,使原来的对象的引用计数+1,其实就是指针拷贝。
所以一般都将NSString设置为copy.

在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:

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

容器

NSArray和NSDictionary可以容纳其他对象的叫做容器类对象。

在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制;对mutable对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下:

[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制

总结

– 容器对象和非容器对象类似,可变对象复制(copy,mutableCopy)的都是一个新对象;不可变对象copy是浅复制,mutableCopy是深复制。
– 对于容器而言,元素对象始终是指针复制。

面试题

  1. NSDictionary如何实现深拷贝?

如果调用NSDictionary的mutableCopy方法,可以得到一个NSMutableDictionary对象,但这只是浅复制,如果我们修改NSDictionary中数组内的值(当然,数组必须是NSMutableArray),会发现,NSMutableDictionary对象内数组的值也跟着更改了。我们需要增加一个mutableDeepCopy方法来实现深复制,在该方法中,循环复制每一个元素。
要实现这一功能,有两种方法,一是继承,二是使用category。category与继承的区别在于,使用category并不是新建一个类,而是在原类的基础上增加一些方法(使用的时候还是用原类名),这样,我们就不需要修改已经在其他源文件中写好的类名,只需要导入h头文件,再把复制方法修改成我们新增的方法即可。

用分类。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#import "NSDictionary+MutableDeepCopy.h"

@implementation NSDictionary (MutableDeepCopy)
-(NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary *dict=[[NSMutableDictionary alloc] initWithCapacity:[self count]];
//新建一个NSMutableDictionary对象,大小为原NSDictionary对象的大小
NSArray *keys=[self allKeys];
for(id key in keys)
{//循环读取复制每一个元素
id value=[self objectForKey:key];
id copyValue;
if ([value respondsToSelector:@selector(mutableDeepCopy)]) {
//如果key对应的元素可以响应mutableDeepCopy方法(还是NSDictionary),调用mutableDeepCopy方法复制
copyValue=[value mutableDeepCopy];
}else if([value respondsToSelector:@selector(mutableCopy)])
{
copyValue=[value mutableCopy];
}
if(copyValue==nil)
copyValue=[value copy];
[dict setObject:copyValue forKey:key];

}
return dict;
}
@end

【扩展】NSMutableArray+DeepCopy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import "NSMutableArray+DeepCopy.h"

@implementation NSMutableArray (DeepCopy)
-(NSMutableArray*) deepCopy
{
NSMutableArray *ret = [[NSMutableArray alloc] initWithCapacity:[self count]];
for (id value in self) {
id oneCopy = nil;
oneCopy = [value copy];
[ret addObject: oneCopy];
}
return ret;
}
@end
  1. 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
  • 因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论传给我的是一个可变对象还是不可变对象,我本身就持有一个不可变的副本。
  • 如果我们使用是 strong ,会持有原来的对象,使原来的对象的引用计数+1,其实就是指针拷贝。浅拷贝。这个属性就可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.

参考:

iOS开发——深拷贝与浅拷贝详解:http://blog.csdn.net/chenyufeng1991/article/details/51771728

iOS 集合的深复制与浅复制https://www.zybuluo.com/MicroCai/note/50592

iOS/Objective-C开发 字典NSDictionary的深复制(使用category)https://www.pocketdigi.com/20120322/720.html

文章目录
  1. 1. 非容器
    1. 1.1. 系统类
    2. 1.2. 自定义类
  2. 2. 容器
  3. 3. 总结
  4. 4. 面试题
本站总访问量 本站访客数人次 ,