通过copy方法可以创建可变对象或不可变对象的不可变副本,对于不可变副本,其对象的值不可以改变。
通过mutableCopy方法可以创建可变对象或不可变对象的可变副本,对于可变副本其对象是可变的。
复制分为浅复制和深复制两种:浅复制只是复制指向对象的指针,并没有复制对象的具体内容。深复制则创建了要复制对象的具体内容,并返回指向对象副本的指针。
对于复制Foundation中的对象,默认并不是深复制,例如copy NSMutableArray对象是浅复制,只是对其指针进行复制;而copy NSMutableString对象是深复制,对原来引用的对象的具体内容也进行了复制。
对于NSString *str,指针str指向对象STRING,假设str指向的内存为2000号,那么2000号内存的内容就是STRING对象的内容。对str指针进行复制时,例如NSString *str2 = [str copy],复制后str2指向的地址也是2000号,因此复制的就是STRING对象的内存地址,实现的是浅复制。测试结果:
NSString *str = @"1"; NSString *str2 = [str copy]; NSLog(@"%p", str); NSLog(@"%p", str2); NSLog(@"%@", str); NSLog(@"%@", str2); str = @"new 1"; // 改变str的指向,不会影响str2的指向 NSLog(@"--------------------"); NSLog(@"%p", str); NSLog(@"%p", str2); NSLog(@"%@", str); NSLog(@"%@", str2);
2014-02-03 11:09:57.332 a[766:303] 0x100001050 2014-02-03 11:09:57.333 a[766:303] 0x100001050 2014-02-03 11:09:57.334 a[766:303] 1 2014-02-03 11:09:57.334 a[766:303] 1 2014-02-03 11:09:57.334 a[766:303] -------------------- 2014-02-03 11:09:57.334 a[766:303] 0x1000010b0 2014-02-03 11:09:57.335 a[766:303] 0x100001050 2014-02-03 11:09:57.335 a[766:303] new 1 2014-02-03 11:09:57.335 a[766:303] 1
对于NSMutableString *mstr,mstr指向对象MSTRING,其内存地址为3000。令NSMutableString *mstr2 = [mstr copy],复制后mstr指向的地址为3010,3010号地址保存着MSTRING的副本,这里实现的是深复制。测试结果:
NSMutableString *mstr = [NSMutableString stringWithString:@"1"]; NSMutableString *mstr2 = [mstr mutableCopy]; NSLog(@"%p", mstr); NSLog(@"%p", mstr2); NSLog(@"%@", mstr); NSLog(@"%@", mstr2); [mstr appendString:@" append"]; // 改变mstr指向的对象的内容,不会影响到mstr2指向的对象副本 NSLog(@"--------------------"); NSLog(@"%p", mstr); NSLog(@"%p", mstr2); NSLog(@"%@", mstr); NSLog(@"%@", mstr2);
2014-02-03 11:10:18.795 a[777:303] 0x100204210 2014-02-03 11:10:18.796 a[777:303] 0x100204320 2014-02-03 11:10:18.796 a[777:303] 1 2014-02-03 11:10:18.797 a[777:303] 1 2014-02-03 11:10:18.797 a[777:303] -------------------- 2014-02-03 11:10:18.797 a[777:303] 0x100204210 2014-02-03 11:10:18.797 a[777:303] 0x100204320 2014-02-03 11:10:18.797 a[777:303] 1 append 2014-02-03 11:10:18.798 a[777:303] 1
对于NSMutableArray *array,假设指针array指向的内存是2000号,那么从2000号开始将连续依次地存放着数组对象的地址,例如2000号地址中保存着对象obj1的地址,2002号地址保存着对象obj2的地址,2004号地址保存着对象obj3的地址。通过内存地址可以访问对象。
对array指向的内容进行复制,例如NSMutableArray *array2 = [array copy],将另外为array2分配一块内存空间,假设复制后的array2指向2010号,其中2010号地址保存着对象指针array[0]的副本(指向obj1),2012号地址保存着指针array[1]的副本(指向obj2)等。换言之,复制的是对象指针的集合,并没有对真正的对象obj1等进行复制,所以实现的是浅复制。
测试结果:
(1)直接赋值,marray和marray2指向同一块内存区域,该内存区域上保持着指向对象的指针集合
NSMutableArray *marray = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil]; NSMutableArray *marray2 = marray; // 直接赋值 marray[0] = @"new 1"; // 改变marray[0]的指向,将会影响到marray2[0]的指向 NSLog(@"marray指向%p, 指向的内容:%@", marray, marray); NSLog(@"marray2指向%p,指向的内容:%@", marray2, marray2); marray = [NSMutableArray arrayWithObjects:@"4", @"5", @"6", nil]; // 改变marray的指向,将不会影响marray2指向的内容 NSLog(@"marray指向%p, 指向的内容:%@", marray, marray); NSLog(@"marray2指向%p,指向的内容:%@", marray2, marray2);
2014-02-03 11:26:47.406 a[867:303] marray指向0x100201b70, 指向的内容:( "new 1", 2, 3 ) 2014-02-03 11:26:47.408 a[867:303] marray2指向0x100201b70,指向的内容:( "new 1", 2, 3 ) 2014-02-03 11:26:47.408 a[867:303] marray指向0x1002061b0, 指向的内容:( 4, 5, 6 ) 2014-02-03 11:26:47.408 a[867:303] marray2指向0x100201b70,指向的内容:( "new 1", 2, 3 )
NSMutableArray *marray = [NSMutableArray arrayWithObjects: [NSMutableString stringWithString:@"1"], [NSMutableString stringWithString:@"2"], [NSMutableString stringWithString:@"3"], nil]; NSMutableArray *marray2 = [marray copy]; NSMutableString *mstr = marray[0]; [mstr appendString:@" append"]; // 改变marray[0]指向的对象的内容,将会影响到marray2[0]指向的内容 NSLog(@"marray指向%p, 该地址上的指针指向的对象:%@", marray, marray); NSLog(@"marray2指向%p,该地址上的指针指向的对象:%@", marray2, marray2); marray[0] = @"new 1"; // 改变marray[0]的指向,将不会影响到marray2[0]的指向 NSLog(@"marray指向%p, 该地址上的指针指向的对象:%@", marray, marray); NSLog(@"marray2指向%p,该地址上的指针指向的对象:%@", marray2, marray2);
2014-02-03 11:02:47.645 a[682:303] marray指向0x100201b70, 该地址上的指针指向的对象:( "1 append", 2, 3 ) 2014-02-03 11:02:47.646 a[682:303] marray2指向0x1002045e0,该地址上的指针指向的对象:( "1 append", 2, 3 ) 2014-02-03 11:02:47.646 a[682:303] marray指向0x100201b70, 该地址上的指针指向的对象:( "new 1", 2, 3 ) 2014-02-03 11:02:47.646 a[682:303] marray2指向0x1002045e0,该地址上的指针指向的对象:( "1 append", 2, 3 )
NSArray *array = @[@"1", @"2", @"3"]; NSArray *array2 = array; NSArray *array3 = [array copy]; NSLog(@"%p", array); NSLog(@"%p", array2); NSLog(@"%p", array3); NSLog(@"-----------"); array = @[@"4", @"5", @"6"]; // 改变array的指向,不影响array2和array3的指向 NSLog(@"%p", array); NSLog(@"%p", array2); NSLog(@"%p", array3);
2014-02-03 11:11:11.708 a[790:303] 0x100204960 2014-02-03 11:11:11.709 a[790:303] 0x100204960 2014-02-03 11:11:11.709 a[790:303] 0x100204960 2014-02-03 11:11:11.709 a[790:303] ----------- 2014-02-03 11:11:11.709 a[790:303] 0x100300540 2014-02-03 11:11:11.710 a[790:303] 0x100204960 2014-02-03 11:11:11.710 a[790:303] 0x100204960
再看一下复制后的各种情况:
1.NSMutableArray和NSArray
NSMutableArray *marray1 = [NSMutableArray arrayWithObjects: [NSMutableString stringWithString:@"1"], [NSMutableString stringWithString:@"2"], [NSMutableString stringWithFormat:@"3"], nil]; NSMutableArray *marray2 = [marray1 mutableCopy]; NSArray *array1 = [marray1 copy]; NSArray *array2 = [marray1 mutableCopy];
(1)添加字符串到marray1[0]中:
// append string to marray1[0] NSMutableString *mstr = marray1[0]; [mstr appendString:@" append"]; NSLog(@"append string to marray1[0]"); NSLog(@"marray1 = %@", marray1); NSLog(@"marray2 = %@", marray2); NSLog(@"array1 = %@", array1); NSLog(@"array2 = %@", array2);
2014-01-31 21:10:39.097 Array[298:303] append string to marray1[0] 2014-01-31 21:10:39.098 Array[298:303] marray1 = ( "1 append", 2, 3 ) 2014-01-31 21:10:39.098 Array[298:303] marray2 = ( "1 append", 2, 3 ) 2014-01-31 21:10:39.098 Array[298:303] array1 = ( "1 append", 2, 3 ) 2014-01-31 21:10:39.099 Array[298:303] array2 = ( "1 append", 2, 3 )
分析:
将marray1[0]指向的对象地址赋给mstr指针,并对mstr指向的对象进行修改,将造成对象层次的修改,所以append会影响到所有数组的输出。
(2)为marray1[0]设置新的对象值:(注意(2)、(3)和(1)是相互独立的,下同)
// set new object to marray1[0] marray1[0] = [NSMutableString stringWithString:@"new 1"]; NSLog(@"set new object to marray1[0]"); NSLog(@"marray1 = %@", marray1); NSLog(@"marray2 = %@", marray2); NSLog(@"array1 = %@", array1); NSLog(@"array2 = %@", array2);
2014-01-31 20:50:52.003 Array[4151:303] set new object to marray1[0] 2014-01-31 20:50:52.004 Array[4151:303] marray1 = ( "new 1", 2, 3 ) 2014-01-31 20:50:52.005 Array[4151:303] marray2 = ( 1, 2, 3 ) 2014-01-31 20:50:52.005 Array[4151:303] array1 = ( 1, 2, 3 ) 2014-01-31 20:50:52.005 Array[4151:303] array2 = ( 1, 2, 3 )
分析:
这里创建了一个新的NSMutableString对象并将其内存地址赋给指针marray1[0],而其他数组指针指向的对象不变,所以输出时其他数组不受影响。
(3)添加新的对象到marray1中:
// add new object to marray1 [marray1 addObject:[NSMutableString stringWithString:@"4"]]; NSLog(@"add new object to marray1"); NSLog(@"marray1 = %@", marray1); NSLog(@"marray2 = %@", marray2); NSLog(@"array1 = %@", array1); NSLog(@"array2 = %@", array2);
2014-01-31 20:51:46.867 Array[4167:303] add new object to marray1 2014-01-31 20:51:46.869 Array[4167:303] marray1 = ( 1, 2, 3, 4 ) 2014-01-31 20:51:46.869 Array[4167:303] marray2 = ( 1, 2, 3 ) 2014-01-31 20:51:46.869 Array[4167:303] array1 = ( 1, 2, 3 ) 2014-01-31 20:51:46.869 Array[4167:303] array2 = ( 1, 2, 3 )
分析:
这里创建了一个新的可变字符串对象,并将其内存地址保存到指针marray1[3]中。而其他几个数组指针的引用不受影响。
(4)操作数组的引用
如果要单独替换marray1[0]指向的对象,应该先创建一个对象,然后再替换marray1[0]的引用。这样就不会对marray2[0]等指向的对象造成影响。
NSMutableArray *marray1 = [NSMutableArray arrayWithObjects: [NSMutableString stringWithString:@"1"], [NSMutableString stringWithString:@"2"], [NSMutableString stringWithFormat:@"3"], nil]; NSMutableArray *marray2 = [marray1 mutableCopy]; NSArray *array1 = [marray1 copy]; NSArray *array2 = [marray1 mutableCopy]; NSMutableString *mstr = [NSMutableString stringWithString:marray1[0]]; // 创建新的对象 [mstr appendString:@" append"]; [marray1 replaceObjectAtIndex:0 withObject:mstr]; // 替换marray1[0]的引用 NSLog(@"marray1 = %@", marray1); NSLog(@"marray2 = %@", marray2); NSLog(@"array1 = %@", array1); NSLog(@"array2 = %@", array2);
控制台输出:
2014-01-31 20:09:33.678 Dictionary[3735:303] marray1 = ( "1 append", 2, 3 ) 2014-01-31 20:09:33.679 Dictionary[3735:303] marray2 = ( 1, 2, 3 ) 2014-01-31 20:09:33.680 Dictionary[3735:303] array1 = ( 1, 2, 3 ) 2014-01-31 20:09:33.680 Dictionary[3735:303] array2 = ( 1, 2, 3 )
再看一个例子:
NSMutableArray *marray1 = [NSMutableArray arrayWithObjects: [NSMutableString stringWithString:@"1"], [NSMutableString stringWithString:@"2"], [NSMutableString stringWithFormat:@"3"], nil]; NSMutableArray *marray2 = [marray1 mutableCopy]; NSArray *array1 = [marray1 copy]; NSArray *array2 = [marray1 mutableCopy]; [marray1 removeObjectAtIndex:0]; NSLog(@"marray1 = %@", marray1); NSLog(@"marray2 = %@", marray2); NSLog(@"array1 = %@", array1); NSLog(@"array2 = %@", array2);
控制台输出:
2014-01-31 20:10:40.716 Dictionary[3746:303] marray1 = ( 2, 3 ) 2014-01-31 20:10:40.717 Dictionary[3746:303] marray2 = ( 1, 2, 3 ) 2014-01-31 20:10:40.717 Dictionary[3746:303] array1 = ( 1, 2, 3 ) 2014-01-31 20:10:40.718 Dictionary[3746:303] array2 = ( 1, 2, 3 )[marray1 removeObjectAtIndex:0]移除的是marray1指向的索引结合中的第一个元素,即移除的是对对象的引用,而不是移除数组中的第一个对象。所以不会影响到marray2,array1,array2。
2.NSMutableString和NSString
复制NSMutableString对象测试代码如下:
NSMutableString *mstr1 = [NSMutableString stringWithString:@"1"]; NSMutableString *mstr2 = [mstr1 mutableCopy]; NSString *str1 = [mstr1 copy]; NSString *str2 = [mstr1 mutableCopy]; [mstr1 appendString:@" append"]; NSLog(@"mstr1 = %@", mstr1); NSLog(@"mstr2 = %@", mstr2); NSLog(@"str1 = %@", str1); NSLog(@"str2 = %@", str2);
2014-01-31 20:54:20.423 String[4221:303] mstr1 = 1 append 2014-01-31 20:54:20.424 String[4221:303] mstr2 = 1 2014-01-31 20:54:20.424 String[4221:303] str1 = 1 2014-01-31 20:54:20.424 String[4221:303] str2 = 1
分析:
对mstr1指向的对象STRING1进行复制,这里复制的是对象STRING1本身的内容,也就是mstr2,str1,str2将分别指向对象STRING2,STRING3,STRING4。对STRING1的修改(在后面附加字符串),将不会影响到其他几个引用指向的对象。
可见这里进行的是深复制。
注意不要改变copy后的对象的值,因为copy生成的对象是不可变的,强行修改会导致程序崩溃。
控制台输出为:
2014-02-01 00:57:24.903 Array[792:303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'
3.NSMutableDictionary和NSDictionary
首先创建1个字典(字典中对应3个对象)和4个字典指针:
NSMutableString *obj1 = [NSMutableString stringWithString:@"obj1"]; NSMutableString *obj2 = [NSMutableString stringWithString:@"obj2"]; NSMutableString *obj3 = [NSMutableString stringWithString:@"obj3"]; NSMutableDictionary *mdic1 = [NSMutableDictionary dictionaryWithObjects:@[obj1, obj2, obj3] forKeys:@[@"key1", @"key2", @"key3"]]; NSMutableDictionary *mdic2 = [mdic1 mutableCopy]; NSDictionary *dic1 = [mdic1 copy]; NSDictionary *dic2 = [mdic1 copy];
对字典进行复制也是浅复制。可以将字典的索引看做数组中的下标索引,所以其原理和数组的复制是一样的。
(1)添加字符串到mdic1的key1对应的可变字符串对象后:
// append string to mdic1[@"key1"] NSMutableString *mstr = mdic1[@"key1"]; [mstr appendString:@" append"]; NSLog(@"append string to midc1[@\"key1\"]"); NSLog(@"mdic1 = %@", mdic1); NSLog(@"mdic2 = %@", mdic2); NSLog(@"dic1 = %@", dic1); NSLog(@"dic2 = %@", dic2);
2014-01-31 22:07:29.241 Dictionary[431:303] append string to midc1[@"key1"] 2014-01-31 22:07:29.242 Dictionary[431:303] mdic1 = { key1 = "obj1 append"; key2 = obj2; key3 = obj3; } 2014-01-31 22:07:29.243 Dictionary[431:303] mdic2 = { key1 = "obj1 append"; key2 = obj2; key3 = obj3; } 2014-01-31 22:07:29.243 Dictionary[431:303] dic1 = { key1 = "obj1 append"; key2 = obj2; key3 = obj3; } 2014-01-31 22:07:29.243 Dictionary[431:303] dic2 = { key1 = "obj1 append"; key2 = obj2; key3 = obj3; }
(2)设置新的对象给key1:
// set new object for key1 NSMutableString *nobj1 = [NSMutableString stringWithString:@"new obj1"]; [mdic1 setObject:nobj1 forKey:@"key1"]; NSLog(@"set new object for key"); NSLog(@"mdic1 = %@", mdic1); NSLog(@"mdic2 = %@", mdic2); NSLog(@"dic1 = %@", dic1); NSLog(@"dic2 = %@", dic2);
2014-01-31 22:08:01.617 Dictionary[448:303] set new object for key 2014-01-31 22:08:01.618 Dictionary[448:303] mdic1 = { key1 = "new obj1"; key2 = obj2; key3 = obj3; } 2014-01-31 22:08:01.619 Dictionary[448:303] mdic2 = { key1 = obj1; key2 = obj2; key3 = obj3; } 2014-01-31 22:08:01.619 Dictionary[448:303] dic1 = { key1 = obj1; key2 = obj2; key3 = obj3; } 2014-01-31 22:08:01.619 Dictionary[448:303] dic2 = { key1 = obj1; key2 = obj2; key3 = obj3; }
(3)在字典中添加新的key-value对:
// add new key-value NSMutableString *obj4 = [NSMutableString stringWithString:@"obj4"]; [mdic1 setObject:obj4 forKey:@"key4"]; NSLog(@"add new key-value"); NSLog(@"mdic1 = %@", mdic1); NSLog(@"mdic2 = %@", mdic2); NSLog(@"dic1 = %@", dic1); NSLog(@"dic2 = %@", dic2);
2014-01-31 22:12:14.678 Dictionary[466:303] add new key-value 2014-01-31 22:12:14.679 Dictionary[466:303] mdic1 = { key1 = obj1; key2 = obj2; key3 = obj3; key4 = obj4; } 2014-01-31 22:12:14.679 Dictionary[466:303] mdic2 = { key1 = obj1; key2 = obj2; key3 = obj3; } 2014-01-31 22:12:14.679 Dictionary[466:303] dic1 = { key1 = obj1; key2 = obj2; key3 = obj3; } 2014-01-31 22:12:14.680 Dictionary[466:303] dic2 = { key1 = obj1; key2 = obj2; key3 = obj3; }
小结:
1.浅复制复制的是指向对象的指针,对指针的修改并不会影响到指向的对象,更不会影响到其他指针。深复制复制的是对象的内容,对对象的操作修改将影响到所有指向该对象的指针。
2.NSMutableArray和NSMutableDictionary的复制是浅复制,NSMutableString的复制是深复制。由于数组或字典中对象数目或大小可能非常大,所以对对象的复制可能引起大量开销,因此这里只复制指针可以节省开销。