面试题

iOS 面试解析|对深浅拷贝的理解

2021-11-25  本文已影响0人  iOS丶lant

对深浅拷贝的理解

我们先要理解拷贝的目的:产生一个副本对象,跟源对象互不影响。

深拷贝和浅拷贝的区别

拷贝类型 拷贝方式 特点
深拷贝 内存拷贝,让副本对象指针和源对象指针指向 两片 内容相同的内存空间。 1. 不会增加被拷贝对象的引用计数;2. 产生了一个内存分配,出现了两块内存。
浅拷贝 指针拷贝,对内存地址的复制,让副本对象指针和源对象指针指向 同一片 内存空间。 1. 会增加被拷贝对象的引用计数;2. 没有进行新的内存分配。注意:如果是小对象如 NSString,可能通过 Tagged Pointer 来存储,没有引用计数。

简而言之:

在 iOS 中对 mutable 对象与 immutable 对象进行 copy 与 mutableCopy 的结果

iOS 提供了 2 个拷贝方法:

对 mutable 对象与 immutable 对象进行 copy 与 mutableCopy 的结果:

源对象类型 拷贝方式 副本对象类型 拷贝类型(深/浅)
mutable 对象 copy 不可变 深拷贝
mutable 对象 mutableCopy 可变 深拷贝
immutable 对象 copy 不可变 浅拷贝
immutable 对象 mutableCopy 可变 深拷贝

注:这里的 immutable 对象与 mutable 对象指的是系统类 NSArray、NSDictionary、NSSet、NSString、NSData 与它们的可变版本如 NSMutableArray 等。

一个记忆技巧就是:对 immutable 对象进行 copy 操作是 浅拷贝,其它情况都是 深拷贝

我们还可以根据拷贝的目的加深理解:

使用 copy、mutableCopy 对集合对象进行的深浅拷贝是针对集合对象本身的

使用 copy、mutableCopy 对集合对象(Array、Dictionary、Set)进行的深浅拷贝是针对集合对象本身的,对集合中的对象执行的默认都是浅拷贝。也就是说只拷贝集合对象本身,而不复制其中的数据。主要原因是,集合内的对象未必都能拷贝,而且调用者也未必想在拷贝集合时一并拷贝其中的每个对象。

如果想要深拷贝集合对象本身的同时,也对集合内容进行 copy 操作,可使用类似以下的方法,copyItems 传 YES。集合中的每个对象都会收到 copyWithZone: 消息,所以需要注意的是集合中的对象必须都符合 NSCopying 协议,否则会导致 Crash。

NSArray *deepCopyArray = [[NSArray alloc]initWithArray:someArray copyItems:YES];

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。
注:initWithArray:copyItems: 方法不是所有情况下都深拷贝集合对象本身的。如果执行 [[NSArray alloc]initWithArray:@[] copyItems:aBoolValue];,也就是源对象为不可变的空数组的话,对源对象本身执行的是浅拷贝,苹果对 @[] 使用了享元。

但是,如果集合中的对象的 copy 操作是浅拷贝,那么对于集合来说还不是真正意义上的深拷贝。比如,你需要对一个 NSArray<NSArray *> 对象进行真正的深拷贝,那么内层数组及其内容也应该执行深拷贝,可以对该集合对象进行 归档 然后 解档,只要集合中的对象都符合 NSCoding 协议。而且,使用这种方式,无论集合中存储的模型对象嵌套多少层,都可以实现深拷贝,但前提是嵌套的子模型也需要符合 NSCoding 协议才行,否则会导致 Crash。

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

需要注意的是,使用 initWithArray:copyItems: 并将 copyItems 传 YES 时,生成的副本集合对象中的对象(下一个级别)是不可变的,所有更深的级别都具有它们以前的可变性。比如以下代码将 Crash。

NSArray *oldArray = @[@[].mutableCopy];
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:oldArray copyItems:YES];
NSMutableArray *mArray = deepCopyArray[0]; // deepCopyArray[0] 已经被深拷贝为 NSArray 对象
[mArray addObject:@""]; // Crash

归档解档集合 的方式会保留所有级别的可变性,就像以前一样。

实现对自定义对象的拷贝

如果想要实现对自定义对象的拷贝,需要遵守 NSCopying 协议,并实现 copyWithZone: 方法。

如果自定义对象支持可变拷贝和不可变拷贝,那么还需要遵守 NSMutableCopying 协议,并实现 mutableCopyWithZone: 方法,返回可变副本。而 copyWithZone: 方法返回不可变副本。使用方可根据需要调用该对象的 copy 或 mutableCopy 方法来进行不可变拷贝或可变拷贝。

以下代码会出现什么问题?

@interface Model : NSObject
@property (nonatomic, copy) NSMutableArray *array;
@end

不论赋值过来的是 NSMutableArray 还是 NSArray 对象,进行 copy 操作后都是 NSArray 对象(深拷贝)。由于属性被声明为 NSMutableArray 类型,就不可避免的会有调用方去调用它的添加对象、移除对象等一些方法,此时由于 copy 的结果是 NSArray 对象,所以就会导致 Crash。

上一篇 下一篇

猜你喜欢

热点阅读