004 copy/mutableCopy 與 淺拷貝/深拷貝

“為學(xué)日益,為道日損,損之又損,以至于無為,無為而無不為”。用自己的思考去理解問題,從問題的源頭探索,才有可能尋找到最接近設(shè)計(jì)者思想的答案。網(wǎng)上有非常多的博客,博客內(nèi)容良莠不齊,且絕大多數(shù)是為被大眾證實(shí),僅僅是一面之詞。日常閱讀,日常解惑,有這兩篇文檔可以閱讀,分別是更新在MRR、ARC時(shí)代的兩篇文章。

Updated: 2009-10-21
Memory Management Programming Guide for Core Foundation
Updated: 2012-07-17
Advanced Memory Management Programming Guide

下面只是筆者對(duì)上面兩篇文章一點(diǎn)讀后感。

引用拷貝(Reference Copy)

Copy Functions

引用拷貝這個(gè)名詞在文章中并沒有直接指出,但有相關(guān)的概念提示,例如截圖第二段中,第三行:copies the reference to the object,第五行中提到:the reference is duplicated,拷貝的結(jié)果是一個(gè)新的指針,故亦稱之為指針拷貝

基本數(shù)據(jù)類型

下面以整數(shù)的拷貝為例:
myInt2 = myInt1

  1. myInt1myInt2 分別是兩個(gè)獨(dú)立的內(nèi)存空間;
  2. myInt1 的值拷貝進(jìn) myInt2

Core Foundation類型

myCFString2 = myCFString1

  1. myCFString2myCFString1 都是CFStringRef類型,僅僅只是拷貝對(duì)象的引用,并不能拷貝對(duì)象的內(nèi)容;
  2. 拷貝一個(gè)對(duì)象的類型(引用)是非常快的,因?yàn)闆]有拷貝對(duì)象的內(nèi)容;
  3. 拷貝一個(gè)可變對(duì)象的的引用是非常危險(xiǎn)的操作。因?yàn)樵谀硞€(gè)地方修改了這個(gè)對(duì)象的數(shù)據(jù),其他地方的引用是無法知道數(shù)據(jù)已經(jīng)被修改了;(數(shù)據(jù)的修改可以是對(duì)象本身被修改,也可以是對(duì)象的某個(gè)屬性被修改)
  4. CFStringCreateCopy拷貝一個(gè)全新的對(duì)象,對(duì)象內(nèi)容與原始數(shù)據(jù)一模一樣,當(dāng)我們?cè)谛薷倪@個(gè)新對(duì)象時(shí),是不會(huì)對(duì)舊對(duì)象造成任何影響;

淺拷貝(Shallow Copy)

Shallow Copy

上面提到了 CFStringCreateCopy,這個(gè)方法僅僅是適用于字符串的copy,而對(duì)于array,同樣有CFArrayCreateCopy方法。CFxxtypexxCreateCopy系列的方法都會(huì)拷貝一個(gè)新的對(duì)象。

單一對(duì)象

例如CFStringRef、CFDataRef,在使用CFxxtypexxCreateCopy時(shí),都會(huì)拷貝一個(gè)全新的對(duì)象。

容器對(duì)象

  1. 對(duì)容器對(duì)象本身,拷貝一個(gè)全新的容器對(duì)象;
  2. 新容器中的元素,只是拷貝的元素的引用;
  3. 文章給出的解釋是,你只希望有一個(gè)不可變數(shù)組去記錄這些元素,也就是你本身就不想去改變他們,那就沒有必要分配額外的空間。

深拷貝(Deep Copy)

Deep Copy
  1. 深拷貝可以拷貝出一個(gè)全新的容器對(duì)象,對(duì)象本身以及容器對(duì)象里面的元素都會(huì)被拷貝;
  2. CFPropertyListCreateDeepCopy,對(duì)于給定的list,遞歸調(diào)用CFxxtypexxCreateCopy方法,拷貝容器中的元素;
  3. 如果需要深拷貝其他數(shù)據(jù)結(jié)構(gòu),需要自己手動(dòng)遞歸拷貝容器對(duì)象里面的所有元素;
  4. 文章也指出,在遞歸的時(shí)候需要避免遞歸循環(huán)

從上面的一些特點(diǎn)不難分析出,深拷貝是一種概念,而不是某個(gè)具體的操作。不管是集合對(duì)象,還是自定義的實(shí)例對(duì)象,除了對(duì)象本身,他們還依賴一些其他對(duì)象,我們可以通過引用的方式來表示其他對(duì)象。當(dāng)對(duì)象本身被拷貝時(shí),僅僅只會(huì)開辟這個(gè)對(duì)象需要的空間,然后將原對(duì)象中的數(shù)據(jù)一比一復(fù)制一份存到這塊內(nèi)存,到這里就是淺拷貝的過程。如果是深拷貝,還需要把所有引用指向的對(duì)象再拷貝一份,讓引用也指向新的對(duì)象,這樣才能將這個(gè)對(duì)象以及這個(gè)對(duì)象所依賴的其他對(duì)象完全與原來的那份隔離開。
滿足以下兩個(gè)過程的拷貝可以稱作深拷貝:

  1. 拷貝對(duì)象本身;
  2. 遞歸拷貝對(duì)象所引用的對(duì)象;

copy 和 mutableCopy

上面僅僅是對(duì)平時(shí)一直遇到,但沒有理解透徹的概念做了一個(gè)整理,加深對(duì)這些概念的理解。上面的三個(gè)拷貝都是在MRR模式下,Core Foundation框架中的概念,而且有API支持。在ARC模式下,我們遇到最多的其實(shí)是strong/copy/mutableCopy這些關(guān)鍵詞。

object graphs
  1. retain/strong,引用拷貝,對(duì)象的引用計(jì)數(shù)+1。圖中classB對(duì)對(duì)象retain,引用計(jì)數(shù)變?yōu)?;
  2. copy,對(duì)象拷貝,會(huì)拷貝一個(gè)新的對(duì)象。圖中classC對(duì)原對(duì)象copy,拷貝一個(gè)新的對(duì)象,新對(duì)象引用計(jì)數(shù)為1,圖中虛線流程;

這張圖可以佐證上面的一些概念。Foundation框架中的copy只表示一件事——拷貝對(duì)象,所以copy淺拷貝、深拷貝的概念無直接聯(lián)系。在ARC中的copy可以理解為MRR中的CreateCopy——The Create Rule

mutableCopy

/* from CFBag.h */
CF_EXPORT CFBagRef  CFBagCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFBagCallBacks *callBacks);
CF_EXPORT CFMutableBagRef   CFBagCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFBagRef bag);

The CFBag function CFBagCreateMutableCopy has both “Create” and “Copy” in its name. It is a creation function because the function name contains the word “Create”. Note also that the first argument is of type CFAllocatorRef—this serves as a further hint. The “Copy” in this function is a hint that the function takes a CFBagRef argument and produces a duplicate of the object. It also refers to what happens to the element objects of the source collection: they are copied to the newly created bag. The secondary “Copy” and “NoCopy” substrings of function names indicate how objects owned by some source objects are treated—that is, whether they are copied or not.

上述案例介紹了createcopy的規(guī)則,與上面分析的一致,而且完全沒有提到任何關(guān)于mutable相關(guān)的解釋。下面再看一個(gè)API的定義

CFArrayCreateMutableCopy

theArray
The array to copy. The pointer values from the array are copied into the new array. However, the values are also retained by the new array.

  1. CFArrayCreateMutableCopy 方法的第三個(gè)參數(shù),用來拷貝的原數(shù)組;
  2. 原數(shù)組的指針被拷貝到了新的數(shù)組;
  3. 指針?biāo)赶虻膶?duì)象被新的數(shù)組持有;

根據(jù)上面對(duì)深拷貝概念的對(duì)比,發(fā)現(xiàn)mutableCopy并沒有完成深拷貝的過程,僅僅只是一個(gè)淺拷貝的過程。與copy不同的是,返回的容器對(duì)象是可變的,兩者差異僅此而已。對(duì)容器對(duì)象,有以下特點(diǎn):

  1. copymutableCopy都是淺拷貝,只拷貝容器對(duì)象本身,不拷貝元素對(duì)象,只拷貝元素引用;
  2. copy返回的容器是不可變的,mutableCopy返回的對(duì)象是可變的;

那么在Foundation框架中要完成深拷貝,可以使用框架提供的API:

initWithArray:copyItems:

總結(jié)

明確每一種概念所發(fā)生的變化,相關(guān)的問題都將迎刃而解。

  1. 指針拷貝
  2. 引用拷貝
  3. 對(duì)象拷貝
  4. 淺拷貝
  5. 深拷貝

Memory Management Programming Guide for Core Foundation
Advanced Memory Management Programming Guide
The Create Rule
CFArrayCreateMutableCopy
initWithArray:copyItems:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,654評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

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