浅拷贝与深拷贝
浅拷贝:指针拷贝,不产生新的对象,源对象的引用计数器+1
-
深拷贝:对象拷贝,会产生新的对象,源对象的引用计数器不变
copy与mutableCopy
-
copy:拷贝的结果是一个不可变(imutable)的对象, 无论源对象是可变的还是不可变的,copy之后的都是不可变的类型
不可变类型 变量名 = [不可变类型 copy];
不可变类型 变量名 = [可变类型 copy];
-
mutableCopy:可变拷贝的结果的数据类型是一个可变的对象,无论源对象时不可变的还是可变的,可变拷贝之后的数据类型都是可变类型
可变类型 变量名 = [不可变类型 mutableCopy];
可变类型 变量名 = [可变类型 mutableCopy];
-
copy对引用计数器的影响:
拷贝一个不可变的类型的结果是新对象和源对象都指向同一个内存地址,即使指针拷贝,属于浅拷贝,所以不生产新对象,源对象的引用计数+1
拷贝一个可变的类型,会生成一个新对象,不影响源对象的引用计数
-
mutableCopy对引用计数器的影响:
- 无论对可变类型或者对不可变类型使用mutableCopy操作,都不会影响源对象的引用计数
copy与mutableCopy的使用
-
- 系统非容器类对象
- 不可变类型
NSString *str = @"123"; NSString *strCopy = [str copy]; NSMutableString *mustr = [str copy]; NSMutableString *mustrCopy = [str mutableCopy]; // [mustr appendString:@"4"]; //会崩溃 [mustrCopy appendString:@"5"]; NSLog(@"str = %@,str地址 = %p,strCopy = %@,strCopy地址 = %p ",str,str,strCopy,strCopy); NSLog(@"mustr = %@,mustr地址 = %p,mustrCopy = %@,mustrCopy地址 = %p",mustr ,mustr,mustrCopy,mustrCopy); /** str = 123,str地址 = 0x102ad4098,strCopy = 123,strCopy地址 = 0x102ad4098 mustr = 123,mustr地址 = 0x102ad4098,mustrCopy = 1235,mustrCopy地址 = 0x1c444bca0 */
- 可变类型
NSMutableString *mustr1 = [NSMutableString stringWithFormat:@"123"]; NSString *immutableStrCopy = [mustr1 copy]; NSMutableString *mutableStr = [mustr1 copy]; NSMutableString *mutableStrCopy = [mustr1 mutableCopy]; // [mutableStr appendString:@"5"]; //会崩溃 [mutableStrCopy appendString:@"6"]; NSLog(@"mustr1 = %@,mustr1地址 = %p,immutableStrCopy = %@,immutableStrCopy地址 = %p ",mustr1,mustr1,immutableStrCopy,immutableStrCopy); NSLog(@"mutableStr = %@,mutableStr地址 = %p,mutableStrCopy = %@,mutableStrCopy地址 = %p",mutableStr ,mutableStr,mutableStrCopy,mutableStrCopy); /** mustr1 = 123,mustr1地址 = 0x1c0247c80,immutableStrCopy = 123,immutableStrCopy地址 = 0xa000000003332313 mutableStr = 123,mutableStr地址 = 0xa000000003332313,mutableStrCopy = 1236,mutableStrCopy地址 = 0x1c0247c20 */
总结
对系统非容器类不可变对象调用Copy方法其实只是把当前对象的指针指向了原对象的地址。
调用mutableCopy方法则是新分配了一块内存区域并把新对象的指针指向了这块区域。
对于可变对象来说调用Copy和MutableCopy方法都会重新分配一块内存。
copy和mutableCopy的区别在于copy在复制对象的时候其实是返回了一个不可变对象,因此当调用方法改变对象的时候会崩溃。
-
- 系统容器类对象
-
不可变类型
NSArray *array = [NSArray arrayWithObjects:@"1",@"2",@"3", nil]; NSArray *arrayCopy = [array copy]; NSMutableArray *musarray = [array copy]; NSMutableArray *musarrayCopy = [array mutableCopy]; NSLog(@"array = %@,array地址 = %p,arrayCopy = %@,arrayCopy地址 = %p ",array,array,arrayCopy,arrayCopy); NSLog(@"musarray = %@,musarray地址 = %p,musarrayCopy = %@,musarrayCopy地址 = %p",musarray ,musarray,musarrayCopy,musarrayCopy); /** array = ( 1, 2, 3 ),array地址 = 0x1c44508f0, arrayCopy = ( 1, 2, 3 ),arrayCopy地址 = 0x1c44508f0 musarray = ( 1, 2, 3 ),musarray地址 = 0x1c44508f0, musarrayCopy = ( 1, 2, 3 ),musarrayCopy地址 = 0x1c4653c20 */
-
可变类型
NSMutableArray *muarray = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"1"],@"2",@"3", nil]; NSArray *immutableArrayCopy = [muarray copy]; NSArray *mutableArrayCopy = [muarray mutableCopy]; NSMutableArray *mutArray = [muarray copy]; NSMutableArray *mutArrayCopy = [muarray mutableCopy]; NSLog(@"muarray = %@,muarray地址 = %p,immutableArrayCopy = %@,immutableArrayCopy地址 = %p,mutableArrayCopy = %@, mutableArrayCopy地址 = %p",muarray,muarray,mutableArrayCopy,mutableArrayCopy,immutableArrayCopy,immutableArrayCopy); NSLog(@"mutArray = %@,mutArray地址 = %p,mutArrayCopy = %@,mutArrayCopy地址 = %p",mutArray ,mutArray,mutArrayCopy,mutArrayCopy); /** muarray = ( 1, 2, 3 ),muarray地址 = 0x6040004434b0, immutableArrayCopy = ( 1, 2, 3 ),immutableArrayCopy地址 = 0x604000443750, mutableArrayCopy = ( 1, 2, 3 ), mutableArrayCopy地址 = 0x604000443540 mutArray = ( 1, 2, 3 ),mutArray地址 = 0x604000443ba0, mutArrayCopy = ( 1, 2, 3 ),mutArrayCopy地址 = 0x604000443600 */ //对于可变对象不管调用Copy还是MutableCopy都是新分配一块内存。但是虽然重新分配了一块内存,但是对象里面的数据依然是指针复制的 NSMutableString *mustr = muarray[0]; [mustr appendString:@"2"]; NSLog(@"muarray = %@,muarray地址 = %p,immutableArrayCopy = %@,immutableArrayCopy地址 = %p,mutableArrayCopy = %@, mutableArrayCopy地址 = %p",muarray,muarray,mutableArrayCopy,mutableArrayCopy,immutableArrayCopy,immutableArrayCopy); NSLog(@"mutArray = %@,mutArray地址 = %p,mutArrayCopy = %@,mutArrayCopy地址 = %p",mutArray ,mutArray,mutArrayCopy,mutArrayCopy); /** muarray = ( 12, 2, 3 ),muarray地址 = 0x6040004434b0, immutableArrayCopy = ( 12, 2, 3 ),immutableArrayCopy地址 = 0x604000443750, mutableArrayCopy = ( 12, 2, 3 ), mutableArrayCopy地址 = 0x604000443540 mutArray = ( 12, 2, 3 ),mutArray地址 = 0x604000443ba0, mutArrayCopy = ( 12, 2, 3 ),mutArrayCopy地址 = 0x604000443600 */ //可以看到当更改原数组的值之后,所有新数组的值都更改了,即使调用了MutableCopy方法创建的新数组里面的值也因此更改,所以可以看出对于系统容器类对象,其元素对象始终是指针复制
-
总结
对系统容器类不可变对象调用Copy方法其实只是把当前对象的指针指向了原对象的地址。
调用mutableCopy方法则是新分配了一块内存区域并把新对象的指针指向了这块区域。
对于系统容器类可变对象来说调用Copy和MutableCopy方法都会重新分配一块内存。
对于系统容器类可变对象,虽然重新分配了一块内存,但是对象里面的数据依然是指针复制的。
-
- 自定义对象
在iOS中并不是所有对象都支持Copy和MutableCopy,遵循NSCopying协议的类可以发送Copy协议,遵循NSMutableCopying协议的类可以发送MutableCopy消息。
如果一个对象没有遵循这两个协议而发送Copy或者MutableCopy消息那么会发生异常。
如果要遵循NSCopying协议,那么必须实现copyWithZone方法。
如果要遵循NSMutableCopying协议那么必须实现mutableCopyWithZone方法。
对于自定义对象来说调用Copy和MutableCopy方法都会重新分配一块内存。
// Man.h
#import <Foundation/Foundation.h>
@interface Man : NSObject<NSCopying,NSMutableCopying>
@property (nonatomic,strong)NSString *name;
@property (nonatomic,assign)NSInteger year;
@end
// Man.m
#import "Man.h"
@implementation Man
#pragma mark description方法内部不能打印self,不然会造成死循环
- (NSString *)description {
return [NSString stringWithFormat:@"[name = %@,year = %ld]", _name,_year];
}
//自定义深拷贝,实现copyWithZone方法
-(id)copyWithZone:(NSZone *)zone{
Man *newMan = [[[self class] allocWithZone:zone] init];
newMan.name = self.name;
newMan.year = self.year;
return newMan;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
Man *newMan = [[[self class] allocWithZone:zone] init];
newMan.name = self.name;
newMan.year = self.year;
return newMan;
}
@end
//调用
Man *man = [[Man alloc]init];
man.name = @"张三";
man.year = 1;
Man *newMan = [man copy];
Man *newMutMan = [man mutableCopy];
NSLog(@"man = %@,man地址 = %p,newMan = %@,newMan地址 = %p,newMutMan = %@, newMutMan地址 = %p",man,man,newMan,newMan,newMutMan,newMutMan);
/**
man = [name = 张三,year = 1],
man地址 = 0x604000036900,
newMan = [name = 张三,year = 1],
newMan地址 = 0x6040004207e0,
newMutMan = [name = 张三,year = 1],
newMutMan地址 = 0x60400003c2a0
*/
newMan.name = @"李四";
NSLog(@"man = %@,man地址 = %p,newMan = %@,newMan地址 = %p,newMutMan = %@, newMutMan地址 = %p",man,man,newMan,newMan,newMutMan,newMutMan);
/**
man = [name = 张三,year = 1],
man地址 = 0x604000036900,
newMan = [name = 李四,year = 1],
newMan地址 = 0x6040004207e0,
newMutMan = [name = 张三,year = 1],
newMutMan地址 = 0x60400003c2a0
*/
newMutMan.name = @"王五";
NSLog(@"man = %@,man地址 = %p,newMan = %@,newMan地址 = %p,newMutMan = %@, newMutMan地址 = %p",man,man,newMan,newMan,newMutMan,newMutMan);
/**
man = [name = 张三,year = 1],
man地址 = 0x604000036900,
newMan = [name = 李四,year = 1],
newMan地址 = 0x6040004207e0,
newMutMan = [name = 王五,year = 1],
newMutMan地址 = 0x60400003c2a0
*/
-
- 总结
-
非集合类对象的 copy 操作:
- [immutableObject copy] 浅复制
- [immutableObject mutableCopy] 深复制
- [mutableObject copy] 深复制
- [mutableObject mutableCopy] 深复制
-
集合类的对象进行 copy 操作:
- [immutableObject copy] 浅复制(对象地址和被复制的对象地址一样)
- [immutableObject mutableCopy] 单层深复制
- [mutableObject copy] 单层深复制
- [mutableObject mutableCopy] 单层深复制
-
自定义对象
如果要遵循NSCopying协议,那么必须实现copyWithZone方法。
如果要遵循NSMutableCopying协议那么必须实现mutableCopyWithZone方法。
[customObject copy] 深复制
[customObject mutableCopy] 深复制
block为什么要使用copy
block使用copy是在MRC中延续下来的,在MRC下,方法内部的block是存放在栈区,使用copy会将block拷贝到堆区。
在ARC下编译器会自动对block进行copy,因此我们使用copy或者strong的效果是一样的。但是我们在ARC下继续使用copy可以提醒我们编译器会自动帮我们实现copy的操作。
参考
iOS 浅拷贝和深拷贝的区别? copy和mutableCopy的区别?