深入理解copy和mutableCopy必须要先理解堆(heap)和栈(stack)的区别,以下链接来自stack overflow的详细解答。简要的一句话就是:
对象保存在堆中,该对象在堆中便有了一个内存地址,该地址属于栈中的一个变量(指针)这个变量在栈中也占有一段内存。
- 不可变对象
对于不可变对象copy是浅拷贝的,mutableCopy是深拷贝
copy
NSString
*test =
@"abc"
;
NSString *copyTest = [test copy ];
NSLog ( @"test(stack) = %p, test(heap) = %p" ,&test, test);
NSString *copyTest = [test copy ];
NSLog ( @"test(stack) = %p, test(heap) = %p" ,&test, test);
NSLog(@"copyTest(stack) = %p, copyTest(heap) = %p", ©Test, copyTest);
打印结果:
test(stack) = 0x7fff5a69baf8, test(heap) = 0x105564068
copyTest(stack) = 0x7fff5a69baf0, copyTest(heap) = 0x105564068
从打印结果来看test和copyTest任然指向堆的同一块内存,只是在栈中拷贝了一个指针变量。属于浅拷贝。当test改变之后copyTest就不再跟它指向同一块堆内存了。
证明如下:
test =
@"cde"
;
NSLog ( @"test(stack) = %p, test(heap) = %p" ,&test, test);
NSLog ( @"test(stack) = %p, test(heap) = %p" ,&test, test);
NSLog(@"copyTest(stack) = %p, copyTest(heap) = %p", ©Test, copyTest);
打印结果:
test(stack) = 0x7fff53eb6af8, test(heap) = 0x10bd490c8
copyTest(stack) = 0x7fff53eb6af0, copyTest(heap) = 0x10bd49068
改变test的内容之后,copyTest任然指向原来的堆栈地址,说明copy能够开辟了新的内存。起到防止源对象改变之后,copy对象不会被改变。
mutableCopy
NSString
*test =
@"abc"
;
NSString *copyTest = [test mutableCopy ];
NSLog ( @"test(stack) = %p, test(heap) = %p" ,&test, test);
NSString *copyTest = [test mutableCopy ];
NSLog ( @"test(stack) = %p, test(heap) = %p" ,&test, test);
NSLog(@"copyTest(stack) = %p, copyTest(heap) = %p", ©Test, copyTest);
打印结果:
test(stack) = 0x7fff59318af8, test(heap) = 0x1068e7068
copyTest(stack) = 0x7fff59318af0, copyTest(heap) = 0x7fe83ae07cb0
mutableCopy之后两个对象的栈地址和堆地址都不同,说明test和copyTest是两个完全不同的对象。
从这里可以理解为什么copy称为浅拷贝,mutableCopy称为深拷贝。
copy拷贝对象之后如果源对象和拷贝对象都不发生改变那么它们都指向同一个堆地址(浅拷贝)。给人一种他们就是两个指针指向同一个对象的赶脚(retain就是这样)。其实不然,当有一个变量改变时他们的堆地址就不同了。说明不是一直指向同一个对象的。而mutableCopy为深拷贝,只要一被执行之后副本的地址就同原来的堆地址就是不一样了称为深拷贝。
- 可变对象
copy
NSMutableString
*test = [
NSMutableString
stringWithFormat
:
@"abc"
];
NSMutableString *copyTest = [test copy ];
NSLog ( @"test(stack) = %p, test(heap) = %p" ,&test, test);
NSMutableString *copyTest = [test copy ];
NSLog ( @"test(stack) = %p, test(heap) = %p" ,&test, test);
NSLog(@"copyTest(stack) = %p, copyTest(heap) = %p", ©Test, copyTest);
打印结果:
test(stack) = 0x7fff53791af8, test(heap) = 0x7fc55b51ffc0
copyTest(stack) = 0x7fff53791af0, copyTest(heap) = 0x7fc55b5204d0
由打印结果看出可变对象copy之后的对象与源对象完全不同,因为NSString是不可变对象,我们虽然可以改变它的内容,改变之后它的地址就会发生改变就不是原来的对象了(这里可能有人会觉得很奇怪为什么说不可变对象,可是我明明可以改变它的内容,别急后面会给出答案!),对不可变对象执行copy会指向同一堆地址就是因为对象的不可变性。那么定义NSMutable对象就说明我们会去改变它,系统就自动的开辟一个新的内存了,内存地址就与原对象不一样。
请看改变可变对象:
NSMutableString
*test = [
NSMutableString
stringWithFormat
:
@"abc"
];
NSMutableString
*copyTest = [test c
opy
];
NSLog ( @"test(stack) = %p, test(heap) = %p test()= %@" ,&test, test, test);
NSLog ( @"copyTest(stack) = %p, copyTest(heap) = %p,copyTest() = %@" , ©Test, copyTest, copyTest);
copy对于可变对象时是深拷贝。
[test
appendString
:
@"d"
];
NSLog ( @"test(stack) = %p, test(heap) = %p , test()=%@" ,&test, test, test);
NSLog ( @"test(stack) = %p, test(heap) = %p , test()=%@" ,&test, test, test);
NSLog(@"copyTest(stack) = %p, copyTest(heap) = %p, copyTest()=%@", ©Test, copyTest, copyTest);
打印结果:
test(stack) = 0x7fff5002daf8, test(heap) = 0x7fcd9af1ceb0, test()= abc
copyTest(stack) = 0x7fff5002daf0, copyTest(heap) = 0x7fcd9af1ab90,copyTest() = abc
test(stack) = 0x7fff5002daf8, test(heap) = 0x7fcd9af1ceb0 , test()=abcd
copyTest(stack) = 0x7fff5002daf0, copyTest(heap) = 0x7fcd9af1ab90, copyTest()=abc
我们改变了源对象test之后,它的堆内存地址竟然没有改变(不是说是可变对象吗,为什么内存地址又不变了啊,我是不是被骗了-。-)。原来可变对象是站在内存的地址的角度来看的。。
可变对象:可以任意的改变对象的内容(家庭成员),不管你怎么改内容我堆内存地址都不会变化(指针一直指针你家的位置)。
不可变对象: 可以为理解为如果对象的内容改变了(abc->abcd),你的堆内存地址就会改变(比如对象变成你朋友了那地址就是他家的地址了),堆地址改变了你也就不是原来的对象了(内容地址都变了),那我原来的对象去哪里了啊,我还要用它怎么办,不好意思找不到了!所以说这个对象是不能变啊,改变了你就再也找不到他了(指针本来指向你家的地址现在指向朋友家的地址了)。论拷贝的作用来了^_^。
总结:浅拷贝拷贝指针(stack),深拷贝拷贝对象(heap也改变)
不可变对象的copy是浅拷贝,mutable是深拷贝
可变对象的copy或mutbleCopy地址都是深拷贝。