1.浅复制与深复制
在Objective-C中,基本数据类型(例如int、float、BOOL等)的复制比较简单,都是会在内存中对需要复制的变量创建一个副本,而对象的复制有两种形式:浅复制和深复制。
浅复制:将原始对象的指针值复制到副本中,即指针复制,原始对象和副本共享引用的数据,相当于创建了一个文件的快捷方式。
深复制:复制原始对象指针所引用的数据,并将其赋给副本对象,即内容复制,相当于创建了一份新的文件。
当为一个类的属性添加copy关键字时,那么对这个属性赋值时,就会执行深复制操作;当把属性关键字改为strong或者weak时,那么对这个属性赋值时,就会执行浅复制(只复制指针地址)。
2.可变对象复制与不可变对象复制
在Foundation框架中,常用的几个类,如NSString、NSArray以及NSDictionary都有其对应的可变子类。当对不同对象进行复制时,系统会采用不同的复制方式,有的采用浅复制,有的采用深复制,因此有必要提前了解对不同类型的对象进行复制时,是指针复制还是值复制。
在NSObject类中提供了两种复制的实例方法,copy和mutableCopy。当对象调用copy方法时,会返回NSCopying协议中的 copyWithZone: 方法的返回值,前提是对象在定义中遵守了NSCopying协议;当对象调用mutableCopy方法时,会返回NSCopying协议中的 mutableCopyWithZone: 方法的返回值,前提是对象在定义中遵守了NSCopying协议。
当对不同类型的对象分别使用copy和mutableCopy方法进行复制时,可能对应不同的复制类型(深复制或浅复制),这取决于类中 copyWithZone: 以及 mutableCopyWithZone: 方法的实现逻辑。
请记住以下结论:
(1)NSString类使用copy为浅复制(指针复制),使用mutableCopy为深复制;NSMutableString类使用copy或者mutableCopy均为深复制。
(2)NSArray类使用copy为浅复制,使用mutableCopy为深复制。另外,不论使用copy还是mutableCopy,容器内的对象都是浅复制。
(3)NSMutableArray类不论使用copy还是mutableCopy都为深复制,容器内的对象都是浅复制。
(4)NSDictionary类使用copy为浅复制,使用mutableCopy为深复制,容器内的对象都是浅复制。
(5)NSMutableDictionary类不论使用copy还是mutableCopy都为深复制,容器内的对象都是浅复制。
3.自定义对象的复制
在实际开发中,对于一些自定义的对象,有时也希望对其进行复制。对于自定义对象的复制,首先要保证在类的定义中遵守NSCopying协议,然后实现 copyWithZone: 方法。对于自定义对象的复制特性(浅复制或深复制),都取决于 copyWithZone: 方法中的实现情况,对于类中定义的属性也需要综合考虑其定义中有关内存管理的特性(strong/weak/copy/assign)。
(1)类的定义与复制
首先自定义ClassA类以及ClassB类,并在ClassB类中,添加4个属性,这4个属性分别使用了copy、strong、weak和assign关键字,如下所示。
为了实现对该类对象的复制,要求ClassB遵守NSCopying协议,同时在.m文件中实现 copyWithZone: 方法,在该方法中的实现逻辑决定了当调用copy方法时对该类对象进行复制所采取的方式(深复制或者浅复制)。
(2)浅复制该类对象
当仅仅需要对该对象进行浅复制时,可以在 copyWithZone: 方法中,直接返回要复制的对象即可。
在ClassB.m文件中,实现 copyWithZone: 方法。
(3)深复制该类对象
当需要对自定义对象深复制时,需要在 copyWithZone: 方法中调用alloc以及init方法,重新开辟一块新的内存空间。另外,对于属性的复制过程中,也需要考虑到属性自身的特性,例如:有copy特性的属性需要重新生成新的副本,strong以及weak只需要做指针复制即可。
在ClassB.m文件中,实现 copyWithZone: 方法。
(4)深复制与浅复制的代码验证
在main.m文件中,添加一个函数,用来复制ClassB的对象,代码如下所示。
当ClassB中的 copyWithZone: 方法中采用浅复制时,复制后得到的副本指向同一内存地址,即进行了指针复制,内容还是原来的内容。
当ClassB中的 copyWithZone: 方法中采用深复制时,可以看到复制得到的对象与原对象的地址不同,同时属性中包含copy关键字的属性在复制过程中也进行了深复制,而strong/weak特性的属性仅仅做了指针复制。