iOS深淺拷貝小結

碼碼碼碼上有錢發表於2018-01-29

iOS開發中,物件的拷貝有兩種方法,就是江湖中流傳的『淺拷貝』與『深拷貝』。淺拷貝意即不拷貝內容的本身,只對其指標進行復制,而深拷貝就是對內容的拷貝了,即是把內容拷貝到一塊新的記憶體中去。apple iOS官方文件中有個圖,一看就明白是咋回事了,如下圖

iOS深淺拷貝小結
簡單來說,淺拷貝就是複製指標,深拷貝就是複製內容。

1、集合的深淺拷貝

    NSArray *contentArray = @[@"鼠",@"牛",@"虎",@"兔",@"龍",@"蛇",@"馬"];
    NSSet *contentSet = [[NSSet alloc] initWithObjects:@"羊",@"猴",@"雞",@"狗",@"豬", nil];
    NSDictionary *contentDic = @{@"shu" : @"鼠",
                                 @"niu" : @"牛",
                                 @"hu" : @"虎",
                                 @"tu" : @"兔",
                                 @"long" : @"龍"};
    /* 淺複製 */
    NSArray *shallowCopyArray = [contentArray copyWithZone:nil];
    NSArray *shallowCopyArray2 = [[NSArray alloc] initWithArray:contentArray copyItems:NO];
    NSSet *shallowCopySet = [contentSet copyWithZone:nil];
    NSSet *shallowCopySet2 = [[NSSet alloc] initWithSet:contentSet copyItems:NO];
    NSDictionary *shallowCopyDic = [contentDic copyWithZone:nil];
    NSDictionary *shallowCopyDic2 = [[NSDictionary alloc] initWithDictionary:contentDic copyItems:NO];
    /* 深複製 */
    NSArray *deepCopyArray = [[NSArray alloc] initWithArray:contentArray copyItems:YES];
    NSSet *deepCopySet = [[NSSet alloc] initWithSet:contentSet copyItems:YES];
    NSDictionary *deepCopyDic = [[NSDictionary alloc] initWithDictionary:contentDic copyItems:YES];
    
    //一組資料可觀察出深淺拷貝
    NSLog(@"contentArray is %p,pointer is %p",contentArray,&contentArray);
    NSLog(@"shallowCopyArray is %p,pointer is %p",shallowCopyArray,&shallowCopyArray);
    NSLog(@"shallowCopyArray2 is %p,pointer is %p",shallowCopyArray2,&shallowCopyArray2);
    NSLog(@"deepCopyArray is %p,pointer is %p",deepCopyArray,&deepCopyArray);
    
    /*列印
     contentArray is 0x604000289b00,pointer is 0x7ffee0d8c7c8
     shallowCopyArray is 0x604000289b00,pointer is 0x7ffee0d8c7b0
     shallowCopyArray2 is 0x604000289b00,pointer is 0x7ffee0d8c7a8
     deepCopyArray is 0x60400028a000,pointer is 0x7ffee0d8c780
     */
複製程式碼

由列印的結果,可以證明淺拷貝與深拷貝的區別了。

2、集合/非集合型別的copy與mutableCopy

除了上面的方法,拷貝物件時還可使用copy、mutableCopy,具體使用如下:

    NSArray *imutableArray = @[@"a",@"b",@"c"];
    NSArray *mutableArray = [NSMutableArray arrayWithObjects:@"d",@"e",@"f",nil];
    NSString *imutableString = @"imutableString";
    NSMutableString *mutableString = [NSMutableString stringWithString:imutableString];
    //
    //imutable物件 copy與mutableCopy
    NSArray *imutable_copyArray = [imutableArray copy]; 
    NSMutableArray *imutable_mutableCopyArray = [imutableArray mutableCopy];
    //[imutable_mutableCopyArray addObject:@"something2"];
    NSLog(@"———————————————————————————集合型別的copy star——————————————————————————————");
    NSLog(@"imutableArray is %p,pointer is %p",imutableArray,&imutableArray);
    NSLog(@"imutable_copyArray is %p,pointer is %p",imutable_copyArray,&imutable_copyArray);
    NSLog(@"imutable_mutableCopyArray is %p,pointer is %p",imutable_mutableCopyArray,&imutable_mutableCopyArray);
    NSLog(@"———————————————————————————end——————————————————————————————");
    //
    NSString *imutable_copyString = [imutableString copy]; 
    NSMutableString *imutable_mutableCopyString = [imutableString mutableCopy];
    [imutable_mutableCopyString appendString:@"someString2"];
    NSLog(@"———————————————————————————非集合型別的copy star——————————————————————————————");
    NSLog(@"imutableString is %p,pointer is %p",imutableString,&imutableString);
    NSLog(@"imutable_copyString is %p,pointer is %p",imutable_copyString,&imutable_copyString);
    NSLog(@"imutable_mutableCopyString is %p,pointer is %p",imutable_mutableCopyString,&imutable_mutableCopyString);
    NSLog(@"———————————————————————————end——————————————————————————————");
    
    //mutable物件 copy與mutableCopy
    NSMutableArray *mutable_copyArray = [mutableArray copy];
    // [mutable_copyArray addObject:@"otherthing"];//runtime crash
    NSMutableArray *mutable_mutableCopyArray = [mutableArray mutableCopy];
    //[mutable_mutableCopyArray addObject:@"otherthing2"];
    NSLog(@"———————————————————————————集合型別的mutableCopy star——————————————————————————————");
    NSLog(@"mutableArray is %p,pointer is %p",mutableArray,&mutableArray);
    NSLog(@"mutable_copyArray is %p,pointer is %p",mutable_copyArray,&mutable_copyArray);
    NSLog(@"mutable_mutableCopyArray is %p,pointer is %p",mutable_mutableCopyArray,&mutable_mutableCopyArray);
    NSLog(@"———————————————————————————end——————————————————————————————");
    //
    NSMutableString *mutable_copyString = [mutableString copy];
    //[mutable_copyString appendString:@"otherString"];//runtime crash
    NSMutableString *mutable_mutableCopyString = [mutableString mutableCopy];
    [mutable_mutableCopyString appendString:@"otherString2"];
    NSLog(@"———————————————————————————非集合型別的mutableCopy star——————————————————————————————");
    NSLog(@"mutableString is %p,pointer is %p",mutableString,&mutableString);
    NSLog(@"mutable_copyString is %p,pointer is %p",mutable_copyString,&mutable_copyString);
    NSLog(@"mutable_mutableCopyString is %p,pointer is %p",mutable_mutableCopyString,&mutable_mutableCopyString);
    NSLog(@"———————————————————————————end——————————————————————————————");
複製程式碼

上面程式碼中有2個runtime crash,分別是

    NSMutableArray *mutable_copyArray = [mutableArray copy];
    // [mutable_copyArray addObject:@"otherthing"];//runtime crash
    以及  
    NSMutableString *mutable_copyString = [mutableString copy];
    //[mutable_copyString appendString:@"otherString"];//runtime crash
複製程式碼

這裡要注意的一個點是,物件在使用copy方法之後,返回來的物件型別是不可變型別的(imutable),儘管你用一個可變型別的物件去接收,然後使用可變型別物件的方法,在執行時會crash,這是因為它終究是一個不可變(imutable)物件,由此可以得出第一個結論:

  • 不管集合、非集合型別物件使用copy返回來的為不可變型別物件,如果對copy返回物件使用mutable物件的介面就會產生runtime crash,而物件使用mutableCopy返回來的則是不可變型別物件。

好,註冊crash程式碼後,再看看這段程式碼的最終列印結果:

iOS深淺拷貝小結
由列印結果,可以得出第二個結論:

  • 對於不可變物件(imutable)使用copy為拷貝指標即淺拷貝,使用mutableCopy為內容拷貝;對於可變物件(mutable)使用copy、mutableCopy都為內容拷貝。

3、集合的深拷貝要注意的地方

先看一段程式碼,及其執行的結果:

    NSLog(@"———————————————————————————imutable集合型別mutableCopy——————————————————————————————");
    NSLog(@"imutableArray pointer:");
    for (NSString *obj in imutableArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }
    NSLog(@"imutable_mutableCopyArray pointer:");
    for (NSString *obj in imutable_mutableCopyArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }

    NSLog(@"———————————————————————————mutable集合型別copy與mutableCopy——————————————————————————————");
    NSLog(@"mutableArray pointer:");
    for (NSString *obj in mutableArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }
    NSLog(@"mutable_copyArray pointer:");
    for (NSString *obj in mutable_copyArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }
    NSLog(@"mutable_mutableCopyArray pointer:");
    for (NSString *obj in mutable_mutableCopyArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }
複製程式碼

列印出來的結果:

iOS深淺拷貝小結
這些集合雖然進行了深拷貝,但是其集合內的元素的內容不變,拷貝的只是元素的指標,所以這並不是完全意義的拷貝。apple官方稱之為『one-level-deep copy』意思是單層的深拷貝,『not real-deep copy』不是真正意義上的深拷貝。

so,第三個結論:

  • 對於可變集合(mutable集合)使用copy/mutableCopy雖說是內容拷貝,但只止於物件,集合元素還是指標拷貝,所以這是單層的深拷貝,並不是完全拷貝。

相關文章