對(duì)象拷貝有兩種方式:淺復(fù)制和深復(fù)制。顧名思義,淺復(fù)制,并不拷貝對(duì)象本身,僅僅是拷貝指向?qū)ο蟮闹羔槪簧顝?fù)制是直接拷貝整個(gè)對(duì)象內(nèi)存到另一塊內(nèi)存中
淺復(fù)制(shallow copy):在淺復(fù)制操作時(shí),對(duì)于被復(fù)制對(duì)象的每一層都是指針復(fù)制。
深復(fù)制(one-level-deep copy):在深復(fù)制操作時(shí),對(duì)于被復(fù)制對(duì)象,至少有一層是深復(fù)制。
完全復(fù)制(real-deep copy):在完全復(fù)制操作時(shí),對(duì)于被復(fù)制對(duì)象的每一層都是對(duì)象復(fù)制。
在非集合類對(duì)象中:對(duì)immutable對(duì)象進(jìn)行copy操作,是指針復(fù)制,mutableCopy操作時(shí)內(nèi)容復(fù)制;對(duì)mutable對(duì)象進(jìn)行copy和mutableCopy都是內(nèi)容復(fù)制。用代碼簡(jiǎn)單表示如下:
[immutableObject copy] // 淺復(fù)制
[immutableObject mutableCopy] //深復(fù)制
[mutableObject copy] //深復(fù)制
[mutableObject mutableCopy] //深復(fù)制
在集合類對(duì)象中,對(duì)immutable對(duì)象進(jìn)行copy,是指針復(fù)制,mutableCopy是內(nèi)容復(fù)制;對(duì)mutable對(duì)象進(jìn)行copy和mutableCopy都是內(nèi)容復(fù)制。但是:集合對(duì)象的內(nèi)容復(fù)制僅限于對(duì)象本身,對(duì)象元素仍然是指針復(fù)制。用代碼簡(jiǎn)單表示如下:
[immutableObject copy] // 淺復(fù)制
[immutableObject mutableCopy] //單層深復(fù)制
[mutableObject copy] //單層深復(fù)制
[mutableObject mutableCopy] //單層深復(fù)制
copy:創(chuàng)建的是不可變副本(NSString,NSArray,NSDictionary)
mutableCopy:創(chuàng)建的是可變副本(NSMutableString,NSMutableArray,NSMutableDictionary)
容器類的深拷貝實(shí)現(xiàn)
- 可以通過歸解檔生成兩份完全獨(dú)立的對(duì)象,但是前提是對(duì)象必須支持 NSCoding
為什么字符串要用copy
上代碼解釋,先用strong
試試
@interface ViewController ()
@property(nonatomic,strong)NSString*whyStringUserCopyStr;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self whyStringUserCopy];
}
-(void)whyStringUserCopy{
NSMutableString *string = [NSMutableString string];
[string appendString:@"hello"];
self.whyStringUserCopyStr = string;
NSLog(@"%@",self.whyStringUserCopyStr);
NSLog(@"string =%p %p whyStringUserCopyStr=%p %p",string,&string,_whyStringUserCopyStr,&_whyStringUserCopyStr);
[string appendString:@"World"];
NSLog(@"%@",self.whyStringUserCopyStr);
}
打印出的信息
hello
string =0x600000245b50 0x7ffee35f92e8 whyStringUserCopyStr=0x600000245b50 0x7f9db3607800
helloWorld
你會(huì)發(fā)現(xiàn)self.whyStringUserCopyStr
只被賦值了一次,卻改變了兩次
在看中間打印出string
的本身的地址和string
指向的地址 以及 whyStringUserCopyStr
的本身的地址和whyStringUserCopyStr
指向的地址
用strong
相當(dāng)于whyStringUserCopyStr
指向了可變字符串的地址,所以可變字符串改變了那么whyStringUserCopyStr
也會(huì)跟著改變,如果用copy
將可變字符串重新拷貝一份,重新開辟內(nèi)存空間,修改可變字符串的值,不會(huì)對(duì)whyStringUserCopyStr造成影響
因?yàn)榭勺儗?duì)象的copy是深拷貝
剛剛的演示,我使用了NSMutableString(可變字符串),對(duì)mutableString執(zhí)行copy操作,屬于深拷貝,所以開辟的新內(nèi)存空間,如果使用的是NSString(不可變內(nèi)存),對(duì)NSString進(jìn)行copy屬于淺拷貝,不會(huì)開辟新的內(nèi)存空間,是不是就不會(huì)出現(xiàn)這個(gè)問題了呢?
接下來(lái)使用NSString來(lái)進(jìn)行演示,將屬性修飾符再次改回strong
#import "ViewController.h"
@interface ViewController ()
/* 為什么字符串使用copy修飾?
這里先不使用copy修飾,NSString是OC對(duì)象,并且是ARC模式,所以先使用strong來(lái)修飾演示
*/
@property (nonatomic,strong) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString string];
str = @"xiaoming";
self.name = str;
NSLog(@"str:%p,name:%p",str,_name);
str = @"xiaogang";
NSLog(@"str:%p,name:%p",str,_name);
NSLog(@"%@",_name);
}
@end
打印
str:0x10c937060,name:0x10c937060
str:0x10c9370a0,name:0x10c937060
xiaoming
從結(jié)果上看,重新設(shè)置str的值后,self.name確實(shí)沒有受到影響
原因:
將之前的可變字符串變?yōu)椴豢勺冏址?因?yàn)镹SString不支持append添加操作,我這里的兩次str賦值操作,其實(shí)是讓str重新指向了一片內(nèi)存空間,并不是修改了str原本內(nèi)存中的值
OC中對(duì)象即指針,實(shí)際上存儲(chǔ)的是內(nèi)存地址,self.name = str;
實(shí)際是將str存儲(chǔ)的@"xiaoming"
這塊地址給了self.name
self.name還指向著@"xiaoming"
所以改變str的指向后,self.name的指向并沒有改變,輸出沒有受到影響