深拷貝和淺拷貝

對(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的指向并沒有改變,輸出沒有受到影響

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容