iOS-深复制(mutableCopy)与浅复制(copy)

浅复制:只复制指向对象的指针,而不复制引用对象本身。对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只是一个指针,对象本身资源还是只有一份(对象引用计数+1),那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改了。

深复制:对象在内存中存在了两份。在Objective-C中并不是所有的对象都支持Copy、MutableCopy,遵守NSCopying协议的类才可以发送Copy消息,遵守NSMutableCopying协议的类才可以发送MutableCopy消息。

一:对非集合类对象的copy操作:

在非集合类对象中:

1、对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;

2、对mutable对象进行copy和mutableCopy都是内容复制。

用代码简单表示如下:

NSString*string =@"origin";

NSString*stringCopy = [string copy];//浅复制复制后的对象不可变

NSMutableString*mStringCopy = [string mutableCopy];//深复制复制后的对象可变

NSLog(@"%p - %p - %p", string, stringCopy, mStringCopy);

2016-03-03 14:56:44.068 Copy[18197:493711] 0x100001080 - 0x100001080 - 0x1003003e0

查看结果:stringCopy和string的内存地址是一样的,mStringCopy和string的内存地址是不一样的。

NSMutableString*string2 = [NSMutableString stringWithString:@"origin2"];

NSString*stringCopy2 = [string2 copy];//深复制复制后的对象不可变

NSMutableString*mStringCopy2 = [string2 mutableCopy];//深复制复制后的对象可变

NSLog(@"%p - %p - %p", string2, stringCopy2, mStringCopy2);

2016-03-03 14:56:44.068 Copy[18197:493711] 0x100103920 - 0x326e696769726f75 - 0x1001039b0

查看结果:mStringCopy2、stringCopy2和string2的内存地址都是不一样的。


二:集合类对象的copy与mutableCopy

NSArray* array =@[@"a", @"b", @"c", @"d"];

NSArray* copyArray = [array copy];//浅复制复制后的对象不可变

NSMutableArray* mCopyArray = [array mutableCopy];//单层深复制复制后的对象可变

NSLog(@"%p - %p - %p", array, copyArray, mCopyArray);

2016-03-03 14:56:44.069 Copy[18197:493711] 0x100600450 - 0x100600450 - 0x1006038e0

查看结果:copyArray和array的内存地址是一样的,mCopyArray和array的内存地址是不一样的。

NSMutableArray* array2 = [NSMutableArray arrayWithObjects: @"a", @"b", @"c", nil];

NSArray* copyArray2 = [array copy];//单层深复制复制后的对象不可变

NSMutableArray*mCopyArray2 = [array mutableCopy];//单层深复制复制后的对象可变

NSLog(@"%p - %p - %p", array2, copyArray2, mCopyArray2);

2016-03-03 14:56:44.069 Copy[18197:493711] 0x100106fd0 - 0x100600450 - 0x100107020

查看结果:mCopyArray2、copyArray2和array2的内存地址都是不一样的。

三:自定义对象的copy

.h文件

@interfaceComplex : NSObject//采用NSCoping协议,实现深层拷贝

{

int_real;

int_imaginary;

}

- (void)setReal:(int)real andImg:(int)img;

- (void)Show;

@end

.m文件

@implementationComplex

-(void)setReal:(int)real andImg:(int)img

{

_real= real;

_imaginary= img;

}

-(void)Show

{

NSLog(@"%d+%di",_real,_imaginary);

}

#pragma mark - NSCopying

-(id)copyWithZone:(NSZone*)zone

{

Complex*p = [ComplexallocWithZone:zone];//申请一块Complex的内存

[psetReal:_realandImg:_imaginary];//拷贝数据

returnp;

}

@end

浅拷贝

Complex*com1 = [[Complex alloc] init];

[com1setReal:12andImg:3];

// 浅拷贝

Complex*com2 = com1;

[com1Show];

[com2Show];

2016-03-03 15:23:43.627 Copy[18424:509266] 12+3i

2016-03-03 15:23:43.627 Copy[18424:509266] 12+3i

[com1setReal:3andImg:5];// com1重新赋值

[com1Show];

[com2Show];

// com1重新赋值,com2随之变化

2016-03-03 15:23:43.627 Copy[18424:509266] 3+5i

2016-03-03 15:23:43.627 Copy[18424:509266] 3+5i

[com2setReal:10andImg:5];// com2重新赋值

[com1Show];

[com2Show];

// com2重新赋值,com1也随之变化

2016-03-03 15:23:43.628 Copy[18424:509266] 10+5i

2016-03-03 15:23:43.628 Copy[18424:509266] 10+5i

深拷贝

Complex*comA = [[Complexalloc]init];

[comAsetReal:2andImg:3];

Complex*comB = [comAcopy];//深层拷贝,使用copy方法,但是前提必须实现NSCopying协议中的copyWithZone方法

[comAShow];

[comBShow];

2016-03-03 15:23:43.628 Copy[18424:509266] 2+3i

2016-03-03 15:23:43.628 Copy[18424:509266] 2+3i

[comAsetReal:3andImg:4];// comA重新赋值

[comAShow];

[comBShow];

// comA改变不引起comB变化

2016-03-03 15:23:43.628 Copy[18424:509266] 3+4i

2016-03-03 15:23:43.628 Copy[18424:509266] 2+3i

[comBsetReal:100andImg:2];// comB重新赋值

[comAShow];

[comBShow];

// comB改变不引起comA变化

2016-03-03 15:23:43.628 Copy[18424:509266] 3+4i

2016-03-03 15:23:43.628 Copy[18424:509266] 100+2i


补充一:

NSString property属性为什么建议用copy不用strong

1、对源头是NSMutableString的字符串,retain仅仅是指针引用,增加了引用计数器,这样源头改变的时候,用这种retain方式声明的变量(无论被赋值的变量是可变的还是不可变的),它也会跟着改变;而copy声明的变量,它不会跟着源头改变,它实际上是深拷贝。

2、对源头是NSString的字符串,无论是retain声明的变量还是copy声明的变量,当第二次源头的字符串重新指向其它的地方的时候,它还是指向原来的最初的那个位置,也就是说其实二者都是指针引用,也就是浅拷贝。这两者对内存计数的影响都是一样的,都会增加内存引用计数,都需要在最后的时候做处理。

3、其实说白了,对字符串为啥要用这两种方式?我觉得还是一个安全问题,比如声明的一个NSString *str变量,然后把一个NSMutableString *mStr变量的赋值给它了,如果要求str跟着mStr变化,那么就用retain;如果str不能跟着mStr一起变化,那就用copy。而对于要把NSString类型的字符串赋值给str,那两都没啥区别。不会影响安全性,内存管理也一样。

补充二:

如果是通过字面量方式创建的的字符串,它存储在数据区,当我们改变copy2的值时,系统会在当前数据区查找是否有相同值的字符串,如果没有就会在数据区创建一个新的字符串,然后指向copy2,所以改变copy2的值,不会影响copy1。

NSString *copy1 = @"copy1";

NSString *copy2 = copy1;

copy2 = @"copy2";

NSLog(@"str = %@ %p", copy1, copy1);

NSLog(@"str = %@ %p", copy2, copy2);

如果是通过对象方式创建字符串,它存储在堆区,此时拷贝是浅拷贝,改变copy2的值,会影响copy1。

NSString *copy1 = [[NSString alloc] initWithString:@"copy1"];

NSString *copy2 = copy1; // 浅拷贝

copy2 = @"copy2";

NSLog(@"str = %@ %p", copy1, copy1);

NSLog(@"str = %@ %p", copy2, copy2);

你可能感兴趣的:(iOS-深复制(mutableCopy)与浅复制(copy))