- 介紹copy和mutableCopy
- 介紹深拷貝與淺拷貝
- block爲何要用copy
- copy相對於直接賦值的好處
- 總結
內存的棧區 : 由編譯器自動分配釋放, 存放函數的參數值, 局部變量的值等. 其操做方式相似於數據結構中的棧. 程序員
內存的堆區 : 通常由程序員分配釋放, 若程序員不釋放, 程序結束時可能由OS回收. 注意它與數據結構中的堆是兩回事, 分配方式卻是相似於鏈表.數組
若是你想要建立一個對象, 該對象與源對象的內容一致, 那麼你能夠考慮用拷貝(copy或mutableCopy), 首先, 我將會利用字符串, 數組, 字典這三種常見的對象來講明copy與mutableCopy的區別. 數據結構
NSString *string = @"Jerry";
[string copy] --> 拷貝出內容爲Jerry的NSString類型的字符串
[string mutableCopy] --> 拷貝出內容爲Jerry的NSMutableString類型的字符串
NSDictionary *dict = @{@"name" : @"Jerry"};
[dict copy] --> 拷貝出內容與dict相同的NSDictionary類型的字典
[dict mutableCopy] --> 拷貝出內容與dict相同的NSMutableDictionary類型的字典
NSArray *array = @[@"Jerry"];
[array copy] --> 拷貝出內容與array相同的NSArray類型的數組
[array mutableCopy] --> 拷貝出內容與array相同的NSMutableArray類型的數組
- copy拷貝出來的對象類型老是不可變類型(例如, NSString, NSDictionary, NSArray等等)
- mutableCopy拷貝出來的對象類型老是可變類型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)
何爲深拷貝, 何爲淺拷貝?函數
深拷貝 : 拷貝出來的對象與源對象地址不一致! 這意味着我修改拷貝對象的值對源對象的值沒有任何影響.
淺拷貝 : 拷貝出來的對象與源對象地址一致! 這意味着我修改拷貝對象的值會直接影響到源對象.測試
這裏須要糾正網上一些錯誤的觀點(如下爲錯誤觀點)3d
copy都是淺拷貝, mutableCopy都是深拷貝
咱們知道, 當咱們用copy從一個可變對象拷貝出一個不可變對象時, 這種狀況就屬於深拷貝而不是淺拷貝!!指針
對於NSString對象, 確實深拷貝就是深拷貝, 淺拷貝就是淺拷貝, 沒有任何異議.
可是對於NSArray, NSDictionary, NSSet這些容器類的對象呢? 固然淺拷貝依然是指針拷貝, 那深拷貝意味着連同容器及其容器內的對象一併拷貝嗎? 仍是隻拷貝容器對象, 對容器內的對象則只是簡單引用呢? 這裏有兩種狀況, 我姑且把它稱爲不徹底深拷貝與徹底深拷貝對象
不徹底深拷貝就是隻拷貝容器對象(拷貝一個殼), 而對於容器內的對象則只保存一份引用. blog
因此咱們知道, 就算咱們修改copyArray不會影響到源array, 可是我經過copyArray修改數組內的object, 對應地源array內的object也會隨之修改, 你們能夠自行測試. 圖片
徹底深拷貝就是連同容器內的對象在內, 完徹底全拷貝一份出來
經過圖片能夠很清楚地知道, 這種狀況下不管是修改copyArray仍是經過copyArray修改數組內的object, 對源array都不會形成半點影響.
// 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是一個對象, 因此block理論上是能夠retain/release的. 可是block在建立的時候它的內存是默認是分配在棧(stack)上, 而不是堆(heap)上的. 因此它的做用域僅限建立時候的當前上下文(函數, 方法...), 當你在該做用域外調用該block時, 程序就會崩潰.
意思就是 : 通常狀況下你不須要自行調用copy或者retain一個block. 只有當你須要在block定義域之外的地方使用時才須要copy. Copy將block從內存棧區移到堆區.
其實block使用copy是MRC留下來的也算是一個傳統吧, 在MRC下, 如上述, 在方法中的block建立在棧區, 使用copy就能把他放到堆區, 這樣在做用域外調用該block程序就不會崩潰. 但在ARC下, 使用copy與strong其實都同樣, 由於block的retain就是用copy來實現的, 因此block使用copy還能裝裝逼, 說明本身是從MRC下走過來的..嘿嘿
看看如下代碼 :
你們猜猜控制檯輸出是啥? 是( Kobe ), ( Kobe, McGrady )嗎?
錯了錯了!!!
array = (
Kobe,
McGragy
), mArray = (
Kobe,
McGragy
)
你們再來猜一下輸出會是什麼?
沒錯!
array = (
Kobe
), mArray = (
Kobe,
McGragy
)
這裏作出了一張圖, 幫助新手弄清楚copy與mutableCopy的區別, 大神請無視^_^
若是可以在你的工程中正確使用copy, 將會對你的程序有不小的幫助.細節決定成敗嘛!!