iOS 之深淺拷貝 Copy

淺拷貝、深拷貝

Copy的作用

  • 在OC中,copy 是利用一個源對象產(chǎn)生一個副本對象,本質(zhì)就是當(dāng)修改源對象的屬性和行為,不會影響副本對象,同樣,當(dāng)修改副本對象的屬性和行為,不會影響源對象。

Copy 的使用

  • 注意: 使用之前必須要遵守 NSCopying 協(xié)議,實現(xiàn) copyWithZone: 方法
其中有兩點需要注意:

1、Foundation類已經(jīng)遵守了<NSCopying>和 <NSMutableCopying>協(xié)議,即實現(xiàn)了copy和mutableCopy方法,因此Foundation對象可以使用這些方法創(chuàng)建對象的副本或可變副本,例如: NSString 、 NSDictionary 、NSArray 、 NSMutableArr ...

2、在iOS中并不是所有的對象都支持copy,mutableCopy,遵守NSCopying 協(xié)議的類可以發(fā)送copy消息,遵守NSMutableCopying 協(xié)議的類才可以發(fā)送mutableCopy消息。假如發(fā)送了一個沒有遵守上訴兩協(xié)議而發(fā)送 copy或者 mutableCopy,那么就會發(fā)生異常。如果想自定義一下copy 那么就必須遵守 NSCopying,并且實現(xiàn) copyWithZone: 方法,如果想自定義一下mutableCopy 那么就必須遵守NSMutableCopying,并且實現(xiàn) mutableCopyWithZone: 方法, 總之想要 Copy 或是mutableCopy 必須要實現(xiàn) copyWithZone: 或是 mutableCopyWithZone: 方法 這兩個方法,協(xié)議不簽訂也沒有問題,只不過不簽訂協(xié)議沒有協(xié)議方法的代碼提示.

例如: 自定義一個 Person 類,當(dāng)操作 copy 時,如果沒有實現(xiàn) NSCopying 協(xié)議,那么會出現(xiàn)錯誤。

    Person *p1 = [[Person alloc]init];
    Person *p2 = [p1 copy];
    NSLog(@"P1 為:%@  P2 為: %@", p1, p2);

自定義類,沒有實現(xiàn) copy 的協(xié)議方法報錯
實現(xiàn)協(xié)議方法,輸出 P1 P2 對象地址
-(id)copyWithZone:(NSZone *)zone
{
    Person *per = [[Person allocWithZone:zone]init];
    return per;
}
查看對象地址,地址不一樣

深淺拷貝對比圖

源對象類型 拷貝方法 副本對象類型 是否產(chǎn)生新對象 拷貝類型
NS* copy NS* NO 淺拷貝(指針拷貝
NS* MutableCopy NSMutable* YES 深拷貝(內(nèi)容拷貝)
NSMutable* copy NS* YES 深拷貝(內(nèi)容拷貝)
NSMutable* MutableCopy NSMutable* YES 深拷貝(內(nèi)容拷貝)
自定義對象:

自定義對象不存在可變和不可變對象,所以均為深拷貝

深拷貝 淺拷貝

通過打印上述 P1 P2 對象,我們發(fā)現(xiàn),內(nèi)存地址是不一樣的(深拷貝),這里我們引入深拷貝和淺拷貝的概念。

淺拷貝

"淺拷貝可以簡單的理解為把對象的引用復(fù)制,或者說對象的指針.指針賦值,使兩個指針指向相同的一塊內(nèi)存空間,操作不安全。“

深拷貝

”深拷貝拷貝當(dāng)前指針指向的對象,系統(tǒng)會隨機給拷貝的對象重新分配一塊內(nèi)存,深拷貝以后,兩份對象的內(nèi)存地址不一樣,指針指向也不一樣。深拷貝會把當(dāng)前容器中的對象重新拷貝一份放到另一個容器中,拷貝后的指針指向新的容器。不僅拷貝了對象還把指針頁拷貝了”

只有從不可變對象copy到不可變對象的時候才是淺拷貝,其他的都是深拷貝

總結(jié)

1、對不可變的對象進行mutableCopy操作,是進行了一次深拷貝,返回的對象是可變的對象。

2、對不可變的對象進行copy操作,進行了一次淺拷貝,返回一個不可變的對象。

3、對可變得對象進行copy,進行了深拷貝,產(chǎn)生了不可變的對象副本。

4、對可變的對象進行了一次mutableCopy,是進行了一次深拷貝, 返回的對象是一個可變的對象。

5、想要讓自定義的對象支持copy和mutableCopy那么就要對應(yīng)實現(xiàn)NSCopying協(xié)議,和NSMutableCopying協(xié)議。

非容器類對象的copy和mutableCopy,以 NSString 為例

    NSString *String = @"字符串";
    self.aNewStr = [String copy];
    self.twoNewStr =[String mutableCopy];
    
    NSLog(@"1*******%p", String);
    NSLog(@"2*******%p", _aNewStr);
    NSLog(@"2*******%p", _twoNewStr);
結(jié)果輸出

2016-06-16 22:54:01.397 oc深淺拷貝[10541:843917] 1*******0x1099bd078
2016-06-16 22:54:01.397 oc深淺拷貝[10541:843917] 2*******0x1099bd078
2016-06-16 22:54:01.398 oc深淺拷貝[10541:843917] 2*******0x7fd959f1d820

通過打印結(jié)果我們可以看出:

  • 通過 Copy 的字符串,不會產(chǎn)生新對象和String的內(nèi)存地址一樣,這里 copy 為淺拷貝(指針拷貝)
  • 通過 mutableCopy 的字符串,產(chǎn)生的 twoNewStr 和原始的字符串 String 地址不一樣,這里產(chǎn)生了新的對象,為深拷貝(內(nèi)容拷貝)

@property 聲明中用 copy 修飾 NSString、NSArray、NSDictionary

因為父類指針可以指向子類對象,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.
如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性.

copy 此特質(zhì)所表達的所屬關(guān)系與 strong 類似。然而設(shè)置方法并不保留新值,而是將其“拷貝” (copy)。 當(dāng)屬性類型為 NSString 時,經(jīng)常用此特質(zhì)來保護其封裝性,因為傳遞給設(shè)置方法的新值有可能指向一個 NSMutableString 類的實例。這個類是 NSString 的子類,表示一種可修改其值的字符串,此時若是不拷貝字符串,那么設(shè)置完屬性之后,字符串的值就可能會在對象不知情的情況下遭人更改。所以,這時就要拷貝一份“不可變” (immutable)的字符串,確保對象中的字符串值不會無意間變動。只要實現(xiàn)屬性所用的對象是“可變的” (mutable),就應(yīng)該在設(shè)置新屬性值時拷貝一份。

NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];

查看內(nèi)存,會發(fā)現(xiàn) string、stringCopy 內(nèi)存地址都不一樣,說明此時都是做內(nèi)容拷貝、深拷貝。即使你進行如下操作:

[string appendString:@"origion!"]

stringCopy 的值也不會因此改變,但是如果不使用 copy,stringCopy 的值就會被改變。 集合類對象以此類推。 所以,

用 @property 聲明 NSString、NSArray、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,是因為他們有對應(yīng)的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操作,為確保對象中的字符串值不會無意間變動,應(yīng)該在設(shè)置新屬性值時拷貝一份。

retain 和 copy 的區(qū)別

 1、retain是對當(dāng)前對象增加了一個指針指向,使對象的引用計數(shù)器加1, 是進行了一次安全的淺拷貝操作。
 2、copy是對當(dāng)前對象進行了一次拷貝,重新拷貝了當(dāng)前對象,當(dāng)使用的時候減少了對當(dāng)前對象的依賴


當(dāng)產(chǎn)生子類時,copyWithZone: 方法需要注意的點

如果你的類產(chǎn)生了子類,那么copyWithZone:方法也將被繼承。

Student *stu = [[Student allocWithZone: zone] init];

該方法應(yīng)該改為:

Student *stu = [[[self class] allocWithZone: zone]init];

如果編寫一個類的copyWithZone:方法,那么子類的方法應(yīng)該先調(diào)用父類的copy方法以復(fù)制繼承來的copy實例變量.

//LIMING 繼承了 Student 在 copy 方法中,要先調(diào)用父類的 copy 方法, super
LIMING *copy = [super copyWithZone:zone];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 本文為轉(zhuǎn)載: 作者:zyydeveloper 鏈接:http://www.lxweimin.com/p/5f776a...
    Buddha_like閱讀 914評論 0 2
  • 一、從面向?qū)ο蟮絆bjective-C概覽copy 1、面向?qū)ο螅?In object-oriented prog...
    adrian920閱讀 775評論 0 3
  • 1、對象拷貝有兩種方式:淺復(fù)制和深復(fù)制。顧名思義,淺復(fù)制,并不拷貝對象本身,僅僅是拷貝指向?qū)ο蟮闹羔槪簧顝?fù)制是直接...
    滴答大閱讀 787評論 0 2
  • 如果說搖滾是奔放硬朗的身子骨,那民謠一定是其中柔軟細(xì)致的靈魂。 聽歌很少,也都相對主流一些。遇上喜歡的,必然循環(huán)百...
    一尾羊閱讀 352評論 0 0
  • 有一句話現(xiàn)在最適合不過,生活越接近平淡,內(nèi)心越接近絢爛。 大學(xué)畢業(yè)后,生活平淡,但是滿足。 年輕的人,心懷都比天高...
    靈翼之光閱讀 197評論 0 0