iOS中的copy 轉載

小結iOS中的copy

http://www.jianshu.com/p/5254f1277dba  轉載自:
  1. 介紹copy和mutableCopy
  2. 介紹深拷貝與淺拷貝
  3. block爲何要用copy
  4. copy相對於直接賦值的好處
  5. 總結

預備知識 :

內存的棧區 : 由編譯器自動分配釋放, 存放函數的參數值, 局部變量的值等. 其操做方式相似於數據結構中的棧. 程序員

內存的堆區 : 通常由程序員分配釋放, 若程序員不釋放, 程序結束時可能由OS回收. 注意它與數據結構中的堆是兩回事, 分配方式卻是相似於鏈表.數組


copy方法和mutableCopy方法

若是你想要建立一個對象, 該對象與源對象的內容一致, 那麼你能夠考慮用拷貝(copy或mutableCopy), 首先, 我將會利用字符串, 數組, 字典這三種常見的對象來講明copy與mutableCopy的區別. 數據結構

NSString

NSString *string = @"Jerry";
[string copy] --> 拷貝出內容爲Jerry的NSString類型的字符串
[string mutableCopy] --> 拷貝出內容爲Jerry的NSMutableString類型的字符串

NSDictionary

NSDictionary *dict = @{@"name" : @"Jerry"};
[dict copy] --> 拷貝出內容與dict相同的NSDictionary類型的字典
[dict mutableCopy] --> 拷貝出內容與dict相同的NSMutableDictionary類型的字典

NSArray

NSArray *array = @[@"Jerry"];
[array copy] --> 拷貝出內容與array相同的NSArray類型的數組
[array mutableCopy] --> 拷貝出內容與array相同的NSMutableArray類型的數組

總結

  1. copy拷貝出來的對象類型老是不可變類型(例如, NSString, NSDictionary, NSArray等等)
  2. mutableCopy拷貝出來的對象類型老是可變類型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)

深拷貝與淺拷貝

何爲深拷貝, 何爲淺拷貝?函數

深拷貝 : 拷貝出來的對象與源對象地址不一致! 這意味着我修改拷貝對象的值對源對象的值沒有任何影響.
淺拷貝 : 拷貝出來的對象與源對象地址一致! 這意味着我修改拷貝對象的值會直接影響到源對象.測試

這裏須要糾正網上一些錯誤的觀點(如下爲錯誤觀點)3d

copy都是淺拷貝, mutableCopy都是深拷貝

咱們知道, 當咱們用copy從一個可變對象拷貝出一個不可變對象時, 這種狀況就屬於深拷貝而不是淺拷貝!!指針

注意 ! 深拷貝與淺拷貝也有相對之分!!!看下面

對於NSString對象, 確實深拷貝就是深拷貝, 淺拷貝就是淺拷貝, 沒有任何異議.
可是對於NSArray, NSDictionary, NSSet這些容器類的對象呢? 固然淺拷貝依然是指針拷貝, 那深拷貝意味着連同容器及其容器內的對象一併拷貝嗎? 仍是隻拷貝容器對象, 對容器內的對象則只是簡單引用呢? 這裏有兩種狀況, 我姑且把它稱爲不徹底深拷貝與徹底深拷貝對象

不徹底深拷貝

不徹底深拷貝就是隻拷貝容器對象(拷貝一個殼), 而對於容器內的對象則只保存一份引用. blog


不徹底深拷貝.png

因此咱們知道, 就算咱們修改copyArray不會影響到源array, 可是我經過copyArray修改數組內的object, 對應地源array內的object也會隨之修改, 你們能夠自行測試. 圖片

徹底深拷貝

徹底深拷貝就是連同容器內的對象在內, 完徹底全拷貝一份出來


徹底深拷貝.png

經過圖片能夠很清楚地知道, 這種狀況下不管是修改copyArray仍是經過copyArray修改數組內的object, 對源array都不會形成半點影響.

ps : 默認狀態下深拷貝指的是不徹底深拷貝, 如要實現徹底深拷貝, 則要重寫copyWithZone: 方法, 自行實現徹底深拷貝的需求. 大致思路以下, 在copyWithZone: 裏對象賦值上不直接賦值而是經過copy方法便可實現, 這裏不做具體討論.既然有朋友問到, 那就貼上示例代碼吧
// Person.m
- (id)copyWithZone:(NSZone *)zone
{
Person *cpyPerson = [[Person allocWithZone:zone] init];
cpyPerson.name = self.name;
cpyPerson.age = self.age;
return cpyPerson;
}
// NSArray
- (id)copy
{
NSArray *cpyArray = [[NSArray alloc] initWithArray:self copyItems:YES];
return cpyArray;
}
// main.m
Person *p1 = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
NSArray *array = @[p1, p2];
NSArray *cpyArray = [array copy];
NSLog(@"%@ - %@", array, cpyArray);
// 輸出結果
(
"<Person: 0x100204af0>",
"<Person: 0x100206b20>"
) - (
"<Person: 0x100207910>",
"<Person: 0x1002074d0>"
)

這樣就能辦到徹底深拷貝的目的了.
ps : 官方文檔說明copy方法內部默認會調用copyWithZone方法的, 可是NSArray由於未知的緣由致使其copy方法不會調用copyWithZone (多是由於OC中已經廢棄了zone這個概念, 蘋果官方文檔有說), 因此這裏我就利用分類重寫了NSArray的copy方法, 實際上蘋果並不推薦這麼作. 


block爲何要用copy?

首先, block是一個對象, 因此block理論上是能夠retain/release的. 可是block在建立的時候它的內存是默認是分配在棧(stack)上, 而不是堆(heap)上的. 因此它的做用域僅限建立時候的當前上下文(函數, 方法...), 當你在該做用域外調用該block時, 程序就會崩潰. 


Apple官方文檔

意思就是 : 通常狀況下你不須要自行調用copy或者retain一個block. 只有當你須要在block定義域之外的地方使用時才須要copy. Copy將block從內存棧區移到堆區.

其實block使用copy是MRC留下來的也算是一個傳統吧, 在MRC下, 如上述, 在方法中的block建立在棧區, 使用copy就能把他放到堆區, 這樣在做用域外調用該block程序就不會崩潰. 但在ARC下, 使用copy與strong其實都同樣, 由於block的retain就是用copy來實現的, 因此block使用copy還能裝裝逼, 說明本身是從MRC下走過來的..嘿嘿


copy相對於直接賦值的好處

看看如下代碼 :


直接賦值

你們猜猜控制檯輸出是啥? 是( Kobe ), ( Kobe, McGrady )嗎?
錯了錯了!!!

 

array = (
Kobe,
McGragy
), mArray = (
Kobe,
McGragy
)

爲何??? 明明可變數組添加對象是在賦值以後, 爲何後面添加對象還會影響到不可變數組呢??
緣由很簡單, 由於Objective-C支持多態.
因此表面上self.array是NSArray對象, 其實骨子裏是NSMutableArray對象.這樣的話將會對後期DEBUG增長很大的成本, 可能會致使莫名其妙的錯誤.
再看如下代碼 : 

使用copy

你們再來猜一下輸出會是什麼?
沒錯!

 

array = (
Kobe
), mArray = (
Kobe,
McGragy
)

這樣就能保證無論賦值的是可變仍是不可變數組, NSArray就是NSArray了!(你爸就是你爸, 不可能變成你了) 

因此你們如今知道爲何@property中的NSString, NSArray, NSDictionary屬性爲何大多時候用copy而不用strong的緣由了麼?


總結

這裏作出了一張圖, 幫助新手弄清楚copy與mutableCopy的區別, 大神請無視^_^


copy與mutableCopy

若是可以在你的工程中正確使用copy, 將會對你的程序有不小的幫助.細節決定成敗嘛!!

相關文章
相關標籤/搜索