在OC編程中桃熄,常常會用到對對象的復(fù)制,然后操作副本對象恨锚。然而對與應(yīng)該選擇何種對象復(fù)制的方式宇驾,復(fù)制后副本對象操作會不會影響原始對象等問題,我們往往沒有過多考慮猴伶,而是憑借經(jīng)驗(yàn)在編碼课舍。接下來就對OC中對象復(fù)制機(jī)制進(jìn)行剖析,通過對復(fù)制機(jī)制的研究可以在編碼中對對象的復(fù)制更加游刃有余他挎。
首先筝尾,在OC中復(fù)制分為深復(fù)制與淺復(fù)制,一個比較認(rèn)可的定義是:
深復(fù)制:復(fù)制對象引用與對象本身办桨。
淺復(fù)制:只復(fù)制對象引用筹淫。
那么哪些操作才是淺復(fù)制,哪些操作是深復(fù)制呢撞?
所有的對象間賦值操作都是淺復(fù)制损姜,僅僅復(fù)制了引用饰剥。如 CopyStr = Str1,這里CopyStr和Str1指向的同一內(nèi)存地址摧阅,改變兩者之間任何一個值汰蓉,另一個都會隨之改變。
試?yán)a:
void assignTest()
{
printf("-----Assign Test-----\n\n");
NSString *str1 = @"Hello";
NSString *str2 = str1;
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str2 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str2);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str2);
printf("\n");
NSMutableString *str3 = [NSMutableString stringWithString:@"Hello"];
NSMutableString *str4 = str3;
[str3 appendString:@" World"];
[str4 appendString:@"!"];
printf("orignalStr : %s\n",[str3 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str4 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str3);
printf("copyStr value address: %p\n",str4);
printf("orignalStr pointer address: %p\n",&str3);
printf("copyStr pointer address: %p\n",&str4);
printf("\n");
NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:@"Hello", nil];
NSMutableArray *arr2 = arr1;
[arr1 addObject:@"World"];
[arr2 addObject:@"!"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr2);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr2);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr2);
printf("\n");
}
輸出結(jié)果:
-----Assign Test-----
orignalStr : Hello
copyStr : Hello
orignalStr value address: 0x100002060
copyStr value address: 0x100002060
orignalStr pointer address: 0x7fff5fbff7a8
copyStr pointer address: 0x7fff5fbff7a0
orignalStr : Hello World!
copyStr : Hello World!
orignalStr value address: 0x1002005c0
copyStr value address: 0x1002005c0
orignalStr pointer address: 0x7fff5fbff798
copyStr pointer address: 0x7fff5fbff790
2016-08-22 11:35:20.312 CopyDemo[2046:72936] orignalArray : (
** Hello,**
** World,**
** "!"**
)
2016-08-22 11:35:20.313 CopyDemo[2046:72936] copyArray : (
** Hello,**
** World,**
** "!"**
)
orignalArray value address: 0x100406910
copyArray value address: 0x100406910
orignalArray pointer address: 0x7fff5fbff788
copyArray pointer address: 0x7fff5fbff780
通過上面結(jié)果我們可以看到逸尖,value的地址都是一樣的古沥,而pointer的地址是不一樣的,這就說明了賦值操作是淺復(fù)制娇跟,只是生成兩份對象的引用岩齿,而對象本身還是同一份。原對象值和副本對象的值操作是相互影響的苞俘。
**那么OC中如何實(shí)現(xiàn)深復(fù)制呢盹沈? **
OC中深復(fù)制是通過copy與mutableCopy方法實(shí)現(xiàn)(但不是都能達(dá)到深復(fù)制的目的),通過copy復(fù)制后的副本都是不可變的吃谣,通過mutableCopy復(fù)制后的副本都是可變的乞封。如初始對象為NSString與NSMutableString,通過copy后副本都是NSString岗憋,而通過mutableCopy后都是NSMutableString肃晚。
接下來分兩種情況討論copy與mutableCopy:
初始對象不可變(如NSString、NSArray等)
調(diào)用copy方法也是淺復(fù)制仔戈,等同于直接賦值关串,因?yàn)閺?fù)制過后的副本和原來的對象都是不可變的,所以調(diào)用copy本質(zhì)就是賦值操作监徘,復(fù)制了引用晋修,但是都指向同一內(nèi)存地址。
調(diào)用mutableCopy是深復(fù)制凰盔,副本成為了可變對象墓卦,但是操作副本,對初始對象的值不會產(chǎn)生影響户敬。
試?yán)a:
void constCopyTest()
{
printf("-----ConstCopy Test-----\n\n");
printf("-NSString Copy-\n");
NSString *str1 = @"Hello";
NSString *str2 = [str1 copy];
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str2 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str2);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str2);
printf("\n");
NSMutableString *str3 = [str1 mutableCopy];
[str3 appendString:@" World!"];
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str3 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str3);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str3);
printf("\n");
printf("-NSArray Copy-\n");
NSArray *arr1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"Hello"], nil];
NSArray *arr2 = [arr1 copy];
[[arr1 objectAtIndex:0] appendString:@" World!"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr2);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr2);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr2);
printf("\n");
NSMutableArray *arr3 = [arr1 mutableCopy];
[arr3 addObject:@"World"];
[[arr1 objectAtIndex:0] appendString:@" + str1"];
[[arr3 objectAtIndex:0] appendString:@" + str3"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr3);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr3);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr3);
printf("\n");
}
輸出結(jié)果:
-----ConstCopy Test-----
-NSString Copy-
orignalStr : Hello
copyStr : Hello
orignalStr value address: 0x100002060
copyStr value address: 0x100002060
orignalStr pointer address: 0x7fff5fbff7a8
copyStr pointer address: 0x7fff5fbff7a0
orignalStr : Hello
copyStr : Hello World!
orignalStr value address: 0x100002060
copyStr value address: 0x1004074d0
orignalStr pointer address: 0x7fff5fbff7a8
copyStr pointer address: 0x7fff5fbff798
-NSArray Copy-
2016-08-22 11:35:20.314 CopyDemo[2046:72936] orignalArray : (
** "Hello World!"**
)
2016-08-22 11:35:20.314 CopyDemo[2046:72936] copyArray : (
** "Hello World!"**
)
orignalArray value address: 0x1001016b0
copyArray value address: 0x1001016b0
orignalArray pointer address: 0x7fff5fbff790
copyArray pointer address: 0x7fff5fbff788
2016-08-22 11:35:20.314 CopyDemo[2046:72936] orignalArray : (
** "Hello World! + str1 + str3"**
)
2016-08-22 11:35:20.314 CopyDemo[2046:72936] copyArray : (
** "Hello World! + str1 + str3",**
** World**
)
orignalArray value address: 0x1001016b0
copyArray value address: 0x100300000
orignalArray pointer address: 0x7fff5fbff790
copyArray pointer address: 0x7fff5fbff780
輸出結(jié)果可以看到落剪,通過copy方法復(fù)制后的副本對象的value地址和原對象是一樣的,所以針對不可變對象是用copy方法是淺復(fù)制尿庐。而mutableCopy方法復(fù)制后忠怖,副本對象的value和pointer地址都和原對象不一樣了,說明mutableCopy方法是深復(fù)制屁倔。
初始對象可變(如NSMutableString脑又、NSMutableArray等)
調(diào)用copy方法是深復(fù)制,因?yàn)檫@里副本是不可變的,所以只考慮初始對象改變问麸。因?yàn)槭巧顝?fù)制往衷,初始對象無論怎么改變,副本的值都是不變的严卖。
調(diào)用mutableCopy也是深復(fù)制席舍,復(fù)制過后,副本與初始對象之間的改變都是獨(dú)立不影響的哮笆,如初始對象str = “example”来颤,str+” append1”,副本str+” append2”稠肘,最終輸出結(jié)果會是初始對象為”example append1”福铅,副本為”example append2”。
試?yán)a:
void mutableCopyTest()
{
printf("-----MutableCopy Test-----\n\n");
printf("-NSString Copy-\n");
NSMutableString *str1 = [NSMutableString stringWithString:@"Hello"];
NSString *str2 = [str1 copy];
[str1 appendString:@" World"];
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str2 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str2);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str2);
printf("\n");
NSMutableString *str3 = [str1 mutableCopy];
[str1 appendString:@" + str1"];
[str3 appendString:@" + str3"];
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str3 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str3);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str3);
printf("\n");
printf("-NSArray Copy-\n");
NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"Hello"], nil];
NSArray *arr2 = [arr1 copy];
[[arr1 objectAtIndex:0] appendString:@" World!"];
[arr1 addObject:@"Word"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr2);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr2);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr2);
printf("\n");
NSMutableArray *arr3 = [arr1 mutableCopy];
[arr1 addObject:@"+arr1"];
[arr3 addObject:@"+arr3"];
[[arr1 objectAtIndex:0] appendString:@" + str1"];
[[arr3 objectAtIndex:0] appendString:@" + str3"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr3);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr3);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr3);
printf("\n");
}
輸出結(jié)果:
-----MutableCopy Test-----
-NSString Copy-
orignalStr : Hello World
copyStr : Hello
orignalStr value address: 0x100300080
copyStr value address: 0x6f6c6c654855
orignalStr pointer address: 0x7fff5fbff7a8
copyStr pointer address: 0x7fff5fbff7a0
orignalStr : Hello World + str1
copyStr : Hello World + str3
orignalStr value address: 0x100300080
copyStr value address: 0x100300320
orignalStr pointer address: 0x7fff5fbff7a8
copyStr pointer address: 0x7fff5fbff798
-NSArray Copy-
2016-08-22 11:35:20.314 CopyDemo[2046:72936] orignalArray : (
** "Hello World!",**
** Word**
)
2016-08-22 11:35:20.314 CopyDemo[2046:72936] copyArray : (
** "Hello World!"**
)
orignalArray value address: 0x100300000
copyArray value address: 0x100300b00
orignalArray pointer address: 0x7fff5fbff790
copyArray pointer address: 0x7fff5fbff788
2016-08-22 11:35:20.314 CopyDemo[2046:72936] orignalArray : (
** "Hello World! + str1 + str3",**
** Word,**
** "+arr1"**
)
2016-08-22 11:35:20.314 CopyDemo[2046:72936] copyArray : (
** "Hello World! + str1 + str3",**
** Word,**
** "+arr3"**
)
orignalArray value address: 0x100300000
copyArray value address: 0x100300e40
orignalArray pointer address: 0x7fff5fbff790
copyArray pointer address: 0x7fff5fbff780
通過輸出結(jié)果可以看到项阴,對于可變對象滑黔,調(diào)用copy與mutableCopy方法都是深復(fù)制,因?yàn)楦北镜膙alue和pointer地址都與原對象不同环揽。
注:對于網(wǎng)上某些解釋說NSArray/NSMutableArray NSDictionary/NSMutableDictionary只有淺復(fù)制略荡,這里認(rèn)為對于對象本身來說調(diào)用mutableCopy或?qū)τ诳勺儗ο笳{(diào)用copy都是深復(fù)制,只能說對于數(shù)組和字典這種復(fù)合結(jié)構(gòu)深復(fù)制操作只是作用到外層對象歉胶,內(nèi)部如果還有可變對象汛兜,僅僅就是引用的復(fù)制。(上面的例子中對于數(shù)組的第一個元素的操作可以很清楚的看出來通今。即使是對數(shù)組的深復(fù)制粥谬,然而改變第一個可變字符串,無論是副本數(shù)組還是原數(shù)組的第一個字符串都改變了衡创。)
完整代碼:
//
// main.m
// CopyDemo
//
// Created by Jiao Liu on 6/23/16.
// Copyright ? 2016 ChangHong. All rights reserved.
//
#import <Foundation/Foundation.h>
void mutableCopyTest()
{
printf("-----MutableCopy Test-----\n\n");
printf("-NSString Copy-\n");
NSMutableString *str1 = [NSMutableString stringWithString:@"Hello"];
NSString *str2 = [str1 copy];
[str1 appendString:@" World"];
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str2 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str2);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str2);
printf("\n");
NSMutableString *str3 = [str1 mutableCopy];
[str1 appendString:@" + str1"];
[str3 appendString:@" + str3"];
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str3 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str3);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str3);
printf("\n");
printf("-NSArray Copy-\n");
NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"Hello"], nil];
NSArray *arr2 = [arr1 copy];
[[arr1 objectAtIndex:0] appendString:@" World!"];
[arr1 addObject:@"Word"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr2);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr2);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr2);
printf("\n");
NSMutableArray *arr3 = [arr1 mutableCopy];
[arr1 addObject:@"+arr1"];
[arr3 addObject:@"+arr3"];
[[arr1 objectAtIndex:0] appendString:@" + str1"];
[[arr3 objectAtIndex:0] appendString:@" + str3"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr3);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr3);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr3);
printf("\n");
}
void constCopyTest()
{
printf("-----ConstCopy Test-----\n\n");
printf("-NSString Copy-\n");
NSString *str1 = @"Hello";
NSString *str2 = [str1 copy];
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str2 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str2);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str2);
printf("\n");
NSMutableString *str3 = [str1 mutableCopy];
[str3 appendString:@" World!"];
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str3 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str3);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str3);
printf("\n");
printf("-NSArray Copy-\n");
NSArray *arr1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"Hello"], nil];
NSArray *arr2 = [arr1 copy];
[[arr1 objectAtIndex:0] appendString:@" World!"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr2);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr2);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr2);
printf("\n");
NSMutableArray *arr3 = [arr1 mutableCopy];
[arr3 addObject:@"World"];
[[arr1 objectAtIndex:0] appendString:@" + str1"];
[[arr3 objectAtIndex:0] appendString:@" + str3"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr3);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr3);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr3);
printf("\n");
}
void assignTest()
{
printf("-----Assign Test-----\n\n");
NSString *str1 = @"Hello";
NSString *str2 = str1;
printf("orignalStr : %s\n",[str1 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str2 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str1);
printf("copyStr value address: %p\n",str2);
printf("orignalStr pointer address: %p\n",&str1);
printf("copyStr pointer address: %p\n",&str2);
printf("\n");
NSMutableString *str3 = [NSMutableString stringWithString:@"Hello"];
NSMutableString *str4 = str3;
[str3 appendString:@" World"];
[str4 appendString:@"!"];
printf("orignalStr : %s\n",[str3 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("copyStr : %s\n",[str4 cStringUsingEncoding:NSUTF8StringEncoding]);
printf("orignalStr value address: %p\n",str3);
printf("copyStr value address: %p\n",str4);
printf("orignalStr pointer address: %p\n",&str3);
printf("copyStr pointer address: %p\n",&str4);
printf("\n");
NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:@"Hello", nil];
NSMutableArray *arr2 = arr1;
[arr1 addObject:@"World"];
[arr2 addObject:@"!"];
NSLog(@"orignalArray : %@",arr1);
NSLog(@"copyArray : %@",arr2);
printf("orignalArray value address: %p\n",arr1);
printf("copyArray value address: %p\n",arr2);
printf("orignalArray pointer address: %p\n",&arr1);
printf("copyArray pointer address: %p\n",&arr2);
printf("\n");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
assignTest();
constCopyTest();
mutableCopyTest();
}
return 0;
}
本文最早發(fā)布于長虹軟服公眾號帝嗡,有興趣的朋友可以去看一下:
剖析【OC】中深復(fù)制與淺復(fù)制