這次探索源自于自己一直以來對<code>ARC</code>的一個疑問,在<code>MRC</code>時代,經常寫下面的代碼:
對象析構時将内部其他對象<code>release</code>掉,申請的非Objc對象的記憶體當然也一并處理掉,最後調用<code>super</code>,繼續将父類對象做析構。而現如今到了<code>ARC</code>時代,隻剩下了下面的代碼:
問題來了:
這個對象執行個體變量(Ivars)的釋放去哪兒了?
沒有顯示的調用<code>[super dealloc]</code>,上層的析構去哪兒了?
<a target="_blank"></a>
A class may provide a method definition for an instance method named dealloc. This method will be called after the final release of the object but before it is deallocated or any of its instance variables are destroyed. The superclass’s implementation of dealloc will be called automatically when the method returns.
大概意思是:dealloc方法在最後一次release後被調用,但此時執行個體變量(Ivars)并未釋放,父類的dealloc的方法将在子類dealloc方法傳回後自動調用
The instance variables for an ARC-compiled class will be destroyed at some point after control enters the dealloc method for the root class of the class. The ordering of the destruction of instance variables is unspecified, both within a single class and between subclasses and superclasses.
了解:ARC下對象的執行個體變量在根類[NSObject dealloc]中釋放(通常root class都是NSObject),變量釋放順序各種不确定(一個類内的不确定,子類和父類間也不确定,也就是說不用care釋放順序)
是以,不用主調<code>[super dealloc]</code>是因為自動調了,後面再說如何實作的;ARC下執行個體變量在根類NSObject析構時析構,下面就探究下。
通過apple的runtime源碼,不難發現NSObject執行<code>dealloc</code>時調用<code>_objc_rootDealloc</code>繼而調用<code>object_dispose</code>随後調用<code>objc_destructInstance</code>方法,前幾步都是條件判斷和簡單的跳轉,最後的這個函數如下:
簡單明确的幹了三件事:
執行一個叫<code>object_cxxDestruct</code>的東西幹了點什麼事
執行<code>_object_remove_assocations</code>去除和這個對象assocate的對象(常用于category中添加帶變量的屬性,這也是為什麼ARC下沒必要remove一遍的原因 (Edit: 在ARC或MRC下都不需要remove,感謝@sagles的基情提示)
執行<code>objc_clear_deallocating</code>,清空引用計數表并清除弱引用表,将所有<code>weak</code>引用指nil(這也就是weak變量能安全置空的所在)
是以,所探尋的ARC自動釋放執行個體變量的地方就在<code>cxxDestruct</code>這個東西裡面沒跑了。
上面找到的名為<code>object_cxxDestruct</code>的方法最終成為下面的調用:
代碼也不難了解,沿着繼承鍊逐層向上搜尋<code>SEL_cxx_destruct</code>這個selector,找到函數實作(<code>void (*)(id)</code>(函數指針)并執行。
搜尋這個selector的聲明,發現是名為<code>.cxx_destruct</code>的方法,以點開頭的名字,我想和unix的檔案一樣,是有隐藏屬性的
ARC actually creates a -.cxx_destruct method to handle freeing instance variables. This method was originally created for calling C++ destructors automatically when an object was destroyed.
和《Effective Objective-C 2.0》中提到的:
When the compiler saw that an object contained C++ objects, it would generate a method called .cxx_destruct. ARC piggybacks on this method and emits the required cleanup code within it.
可以了解到,<code>.cxx_destruct</code>方法原本是為了C++對象析構的,ARC借用了這個方法插入代碼實作了自動記憶體釋放的工作
最好的辦法還是寫個測試代碼把這個隐藏的方法找出來,其實在runtime中運作已經沒什麼隐藏可言了,簡單的類結構如下:
隻有兩個簡單的屬性,找個地方寫簡單的測試代碼:
主要目的是為了讓這個對象走dealloc方法,建立的son對象過了大括号作用域就會釋放了,是以在<code>after new</code>這行son對象初始化完成,在<code>gone</code>這行son對象被dealloc
将這個擴充引入工程内,在<code>after new</code>處設定一個斷點,run,trigger後使用lldb指令用這個擴充輸出Son類所有的方法名:

發現了這個<code>.cxx_destruct</code>方法,經過幾次試驗,發現:
隻有在ARC下這個方法才會出現(試驗代碼的情況下)
隻有目前類擁有執行個體變量時(不論是不是用property)這個方法才會出現,且父類的執行個體變量不會導緻子類擁有這個方法
出現這個方法和變量是否被指派,指派成什麼沒有關系
依然在<code>after new</code>斷點處,輸入lldb指令:
将<code>name</code>的變量加入watchpoint,當這個變量被修改時會觸發trigger:
從中可以看出,在這個時刻,<code>_name</code>從0x00006b98變成了0x0,也就是nil,趕緊看下調用棧:
發現果然跟到了<code>.cxx_destruct</code>方法,而且是在<code>objc_storeStrong</code>的過程中釋放
——-
## 刨根問底.cxx_destruct
知道了ARC下對象執行個體變量的釋放過程在<code>.cxx_destruct</code>内完成,但這個函數内部發生了什麼,是如何調用<code>objc_storeStrong</code>釋放變量的呢?
從上面的探究中知道,<code>.cxx_destruct</code>是編譯器生成的代碼,那它很可能在clang前端編譯時完成,這讓我聯想到clang的<code>Code Generation</code>,因為之前曾經使用<code>clang -rewrite-objc xxx.m</code>時檢視過官方文檔留下了些印象,于是google:
結果發現clang的<code>doxygen</code>文檔中<code>CodeGenModule</code>子產品正是這部分的實作代碼,cxx相關的代碼生成部分源碼在
<a target="_blank" href="http://clang.llvm.org/doxygen/CodeGenModule_8cpp-source.html">http://clang.llvm.org/doxygen/CodeGenModule_8cpp-source.html</a>
位于1827行,删減掉離題部分如下:
這個函數大概作用是:擷取<code>.cxx_destruct</code>的selector,建立Method,并加入到這個Class的方法清單中,最後一行的調用才是真的建立這個方法的實作。這個方法位于
<a target="_blank" href="http://clang.llvm.org/doxygen/CGObjC_8cpp_source.html">http://clang.llvm.org/doxygen/CGObjC_8cpp_source.html</a>
1354行,包含了構造和析構的cxx方法,繼續跟随<code>.cxx_destruct</code>,最終調用<code>emitCXXDestructMethod</code>函數,代碼如下:
分析這段代碼以及其中調用後發現:它周遊目前對象所有的執行個體變量(Ivars),調用<code>objc_storeStrong</code>,從<code>clang</code>的ARC文檔上可以找到<code>objc_storeStrong</code>的示意代碼實作如下:
在<code>.cxx_destruct</code>進行形如<code>objc_storeStrong(&ivar, null)</code>的調用後,這個執行個體變量就被<code>release</code>和設定成<code>nil</code>了
按照上面的思路,自動調用<code>[super dealloc]</code>也一定是<code>CodeGen</code>幹的工作了
<code>StartObjCMethod</code>方法中:
上面代碼可以得知在調用<code>dealloc</code>方法時被插入了代碼,由<code>FinishARCDealloc</code>結構定義:
上面代碼基本上就是向父類轉發<code>dealloc</code>的調用,實作了自動調用<code>[super dealloc]</code>方法。
ARC下對象的成員變量于編譯器插入的<code>.cxx_desctruct</code>方法自動釋放
ARC下<code>[super dealloc]</code>方法也由編譯器自動插入
所謂<code>編譯器插入代碼</code>過程需要進一步了解,還不清楚其運作方式
clang的<code>CodeGen</code>也值得深入研究一下
<a target="_blank" href="http://clang.llvm.org/docs/AutomaticReferenceCounting.html">http://clang.llvm.org/docs/AutomaticReferenceCounting.html</a>
<a target="_blank" href="http://my.safaribooksonline.com/book/programming/objective-c/9780132908641/3dot-memory-management/ch03">http://my.safaribooksonline.com/book/programming/objective-c/9780132908641/3dot-memory-management/ch03</a>
