iOS 之深淺拷貝 Copy

淺拷貝、深拷貝

Copy的作用

  • 在OC中,copy 是利用一個源對象產生一個副本對象,本質就是當修改源對象的屬性和行為,不會影響副本對象,同樣,當修改副本對象的屬性和行為,不會影響源對象。

Copy 的使用

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

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

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

例如: 自定義一個 Person 類,當操作 copy 時,如果沒有實現 NSCopying 協議,那么會出現錯誤。

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

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

深淺拷貝對比圖

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

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

深拷貝 淺拷貝

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

淺拷貝

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

深拷貝

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

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

總結

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

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

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

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

5、想要讓自定義的對象支持copy和mutableCopy那么就要對應實現NSCopying協議,和NSMutableCopying協議。

非容器類對象的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);
結果輸出

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

通過打印結果我們可以看出:

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

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

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

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

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

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

[string appendString:@"origion!"]

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

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

retain 和 copy 的區別

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


當產生子類時,copyWithZone: 方法需要注意的點

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

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

該方法應該改為:

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

如果編寫一個類的copyWithZone:方法,那么子類的方法應該先調用父類的copy方法以復制繼承來的copy實例變量.

//LIMING 繼承了 Student 在 copy 方法中,要先調用父類的 copy 方法, super
LIMING *copy = [super copyWithZone:zone];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容

  • 本文為轉載: 作者:zyydeveloper 鏈接:http://www.lxweimin.com/p/5f776a...
    Buddha_like閱讀 894評論 0 2
  • 一、從面向對象到Objective-C概覽copy 1、面向對象: In object-oriented prog...
    adrian920閱讀 771評論 0 3
  • 1、對象拷貝有兩種方式:淺復制和深復制。顧名思義,淺復制,并不拷貝對象本身,僅僅是拷貝指向對象的指針;深復制是直接...
    滴答大閱讀 781評論 0 2
  • 如果說搖滾是奔放硬朗的身子骨,那民謠一定是其中柔軟細致的靈魂。 聽歌很少,也都相對主流一些。遇上喜歡的,必然循環百...
    一尾羊閱讀 347評論 0 0
  • 有一句話現在最適合不過,生活越接近平淡,內心越接近絢爛。 大學畢業后,生活平淡,但是滿足。 年輕的人,心懷都比天高...
    靈翼之光閱讀 184評論 0 0