天天看點

iOS深淺拷貝

OC對象的三種拷貝方式

OC的對象拷貝有如下三種方式,很多時候我們把深複制和完全複制混為一談,其他他們是有差別的,具體如下:

  • 淺複制(shallow copy):在淺複制操作時,對于被複制對象的每一層都是指針複制。
  • 深複制(one-level-deep copy):在深複制操作時,對于被複制對象,至少有一層是深複制。
  • 完全複制(real-deep copy):在完全複制操作時,對于被複制對象的每一層都是對象複制。
    iOS深淺拷貝
    iOS深淺拷貝

兩圖以避之

了解深複制(mutableCopy)

淺複制很簡單,就不示範了,看上面的圖就懂了,隻是簡單的指針拷貝,是以改變原對象或者拷貝後的對象,都會影響另外一個對象。

從上圖我們可以看到mutableCopy對于任何對象都是内容複制,也就是說進行了深複制。

上代碼:

NSMutableArray * dataArray1=[NSMutableArray arrayWithObjects:
[NSMutableString stringWithString:@"1"],
[NSMutableString stringWithString:@"2"],
[NSMutableString stringWithString:@"3"],
[NSMutableString stringWithString:@"4"],
nil
];
NSMutableArray * dataArray2=[NSMutableArray arrayWithObjects:
[NSMutableString stringWithString:@"one"],
[NSMutableString stringWithString:@"two"],
[NSMutableString stringWithString:@"three"],
[NSMutableString stringWithString:@"four"],
dataArray1,
nil
];
NSMutableArray * dataArray3;
NSMutableString * mStr;
dataArray3=[dataArray2 mutableCopy];
mStr = dataArray2[0];
[mStr appendString:@"--ONE"];
NSLog(@"dataArray3:%@",dataArray3);
NSLog(@"dataArray2:%@",dataArray2);      

輸出如下:

2016-07-31 17:40:30.702 test1[2113:169774] dataArray3:(
    "one--ONE",
    two,
    three,
    four,
        (
        1,
        2,
        3,
        4
    )
)
2016-07-31 17:40:30.703 test1[2113:169774] dataArray2:(
    "one--ONE",
    two,
    three,
    four,
        (
        1,
        2,
        3,
        4
    )
)      

看上面的輸出,我們發現我們改變原數組dataArray2,竟然也會影響深複制後的dataArray3,不是說好的内容複制嗎,為什麼會這樣?

這裡我們來說說深複制和完全複制的差別。

我們知道深複制,就是把原有對象的内容直接克隆一份到新對象,但是這裡有一個坑就是他隻會複制一層對象,而不會複制第二層甚至更深層次的對象。

代碼dataArray3=[dataArray2 mutableCopy];隻是對數組dataArray2本身進行了内容拷貝,但是裡面的字元串對象卻沒有進行内容拷貝,而是進行的淺複制,那麼dataArray2和dataArray3裡面的對象是共享同一份的。是以才會出現上面的情況。

單層深複制

那麼如何解決上面的問題呢?

可以使用如下代碼

dataArray3=[[NSMutableArray alloc]initWithArray:dataArray2 copyItems:YES];      
2016-07-31 17:45:48.472 test1[2151:173221] dataArray3:(
    one,
    two,
    three,
    four,
        (
        1,
        2,
        3,
        4
    )
)
2016-07-31 17:45:48.472 test1[2151:173221] dataArray2:(
    "one--ONE",
    two,
    three,
    four,
        (
        1,
        2,
        3,
        4
    )
)      

可以看到dataArray3并沒有被改變,但是别高興的太早,我們再來改改。

代碼如下:

NSMutableArray * dataArray1=[NSMutableArray arrayWithObjects:
[NSMutableString stringWithString:@"1"],
[NSMutableString stringWithString:@"2"],
[NSMutableString stringWithString:@"3"],
[NSMutableString stringWithString:@"4"],
nil
];
NSMutableArray * dataArray2=[NSMutableArray arrayWithObjects:
[NSMutableString stringWithString:@"one"],
[NSMutableString stringWithString:@"two"],
[NSMutableString stringWithString:@"three"],
[NSMutableString stringWithString:@"four"],
dataArray1,
nil
];
NSMutableArray * dataArray3;
NSMutableString * mStr;
dataArray3=[[NSMutableArray alloc]initWithArray:dataArray2 copyItems:YES];
NSMutableArray *mArr = (NSMutableArray *)dataArray2[4];
mStr = mArr[0];
[mStr appendString:@"--ONE"];
NSLog(@"dataArray3:%@",dataArray3);
NSLog(@"dataArray2:%@",dataArray2);      
2016-07-31 17:47:19.421 test1[2174:174714] dataArray3:(
    one,
    two,
    three,
    four,
        (
        "1--ONE",
        2,
        3,
        4
    )
)
2016-07-31 17:47:19.421 test1[2174:174714] dataArray2:(
    one,
    two,
    three,
    four,
        (
        "1--ONE",
        2,
        3,
        4
    )
)      

可以看到深複制又失效了,這是因為dataArray3=[[NSMutableArray alloc]initWithArray:dataArray2 copyItems:YES];僅僅能進行一層深複制,對于第二層或者更多層的就無效了,那怎麼辦呢?

别急,我們還有大招沒放。

完全複制

要想對多層集合對象進行複制,我們需要進行完全複制,這裡可以使用歸檔和接檔。

實作代碼如下:

dataArray3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:dataArray2]];      

此時輸出如下:

2016-07-31 17:49:55.561 test1[2202:177163] dataArray3:(
    one,
    two,
    three,
    four,
        (
        1,
        2,
        3,
        4
    )
)
2016-07-31 17:49:55.562 test1[2202:177163] dataArray2:(
    one,
    two,
    three,
    four,
        (
        "1--ONE",
        2,
        3,
        4
    )
)      

可以看到dataArray3沒有被dataArray2的修改影響。

類複制

說完了對象的複制,我們來看看如何實作類的複制,因為比較簡單,直接放上代碼

  • 定義類複制
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCopying>
@property(strong,nonatomic)NSString *age;
@property(strong,nonatomic)NSString *name;
@end
#import "Person.h"
@implementation Person
- (id)copyWithZone:(NSZone *)zone
{
    Person *person = [[Person allocWithZone:zone] init];
    person.age = self.age;
    person.name = self.name;
    return person;
}
@end      
  • 調用
Person *person = [[Person alloc]init];
person.age = @"dsdsd";
person.name = @"dsdsdddww";
Person *copyPerson = [person copy];
 NSLog(@"%@-----%@",copyPerson.age, copyPerson.name);      

可以看到copyPerson的兩個屬性和persona一樣。

@property中的copy關鍵字

在設定NSString類型的屬性的時候,我們最好設定為copy類型,這樣别人使用我們定義的屬性的時候,他不管怎麼改動該屬性的指派,都不會影響我們給該屬性賦的值,為什麼呢?

下面我們來看看

iOS深淺拷貝

如上圖所示,string2的屬性是copy類型,可以看到是無法被修改的。

因為此時string2和copystring的記憶體位址不一樣,修改一個,不會影響另外一個。

iOS深淺拷貝

上圖所示,如果string2的屬性是strong類型,就可以被修改,如下圖所示:

因為此時string2和copystring的記憶體位址都是一樣的,修改一個,兩個就同時被修改

copy關鍵字的NSMutableString崩潰

繼續閱讀