《Objective-C 高階程式設計 iOS與OS X多執行緒和記憶體管理》 核心札記一
蘋果原始碼不會告訴你的
——引子
近日偶借一本圖靈出版的程式書籍,是由日本資深軟體工程師 K.S. (Twitter:@splhack) 和 TF (Twitter:@munakoiso) 合作編寫,國內 黎華 譯,全書共三章,分別是 ARC,Blocks 和 Grand Central Dispatch,簡稱GCD ,共186 頁,本以為又是抄襲官方SDK的一本入門級書籍,想隨便翻翻,於是直到清明第一天才翻閱,可是,這完全顛覆了之前所有的相關書籍,之前的iOS教程基本都是教你如何用,怎麼使用這些SDK,而這本書卻是告訴你為什麼要這麼用,比如ARC底層是如何實現的,涉及C,指標等底層程式碼,預測蘋果官方底層程式碼,通過例項驗證預測,知道蘋果的原始碼到底做了什麼,不過,這本書不推薦剛入門的朋友閱讀,適合半年開發以上的朋友進一步瞭解新特性底層如何實現。
核心札記一 ARC篇章 閱讀地點:北京 肯德基店 2014.4.6
1,記憶體管理的實質就是引用計數機制,作者用了進出辦公室開燈,關燈的例項來說明,很形象,不必多言;
2,ARC 作者講到記憶體的管理是由 編譯器 來管理,這個後面作者做了驗證,並不認可全部記憶體都是由編譯器來管理(官方解釋ARC),通過程式碼預測得有時候還是需要OC 執行時庫的協助,即ARC下需要以下工具和庫來實現:a) clang (LLVM編譯器)3.0以上,b) objc4 OC 執行時庫493.9以上;
3,作者這裡對比的底層原始碼來自 GNUstep,藉助它來分析蘋果的底層原始碼,批註:GNUstep(http://gnustep.org)和蘋果的Cocoa(http://opensource.apple.com) 兩者的行為和實現方式站在使用者的角度來說非常相似,理解了GNUstep就等於理解了蘋果的Cocoa實現;
專欄
NSZone是防止記憶體碎片化引入的結構,多重區域分割了記憶體,主要目的是防止記憶體碎片化,但是蘋果官方文件解釋目前的執行時系統簡單的忽略了區域的概念,執行時系統中的記憶體管理本身已經極具效率,故而不在用區域來管理記憶體。
4,推測蘋果是通過散列表管理引用計數,GNU是把引用計數保留在頭部的記憶體變數中,看了起來比較高效,但是蘋果的好處為無需考慮記憶體塊頭部,可以通過記錄表追溯到各物件的記憶體塊,而且如果用工具檢測記憶體洩露時,引用計數的各記錄也有助於檢測各物件的持有者是否存在;
5,作者把區域性變數翻譯成自動變數,有點繞,在物件放入pool後,當pool被drain時候,就對裡面的各個物件傳送release,注意,作者講到NSRunLoop 每次迴圈過程中 NSAutoreleasePool物件被生成或廢棄,故而不要濫用自動釋放,這裡也舉例說明一個大量的迴圈裡面必須使用自動釋放池及時釋放每次迴圈生成的物件,面試題中常見,通過對原始碼的分析,作者得出 autorelease 例項方法的本質就是呼叫NSAutoreleasePool物件的addObject 類方法;
專欄
提高呼叫Objective-C方法的速度,在GNU裡面的autorelease 是用一種特殊的方法實現的,被稱為 IMP Caching,即在框架初始化時對其結果值進行快取,方法呼叫就是使用快取的結果值,這裡有點模糊;
專欄
如果對pool物件傳送 autorelease 將會發生異常,在OC中對一個物件傳送autorelease,都是呼叫NSObject類的autorelease,但是對NSAutoreleasePool類,autorelease例項方法已經被該類過載,因此執行時就要出錯,錯誤:cannot autorelese an atuorelese pool ;
6,ARC的使用從本質上說並沒有改變“引用計數式記憶體管理”,可以指定編譯器為:-fobjc-arc,ARC下的修飾符:__strong,__weak,__unsafe_unretained,__atuoreleasing,a)__strong修飾符是預設,如 id obj ==id __strong obj,即強引用,b)__weak,弱引用, 主要是為解決 ”迴圈引用“ 而使用,引用計數式必然會引起迴圈引用,這是不可避免的,常見為物件之間互相強引用,還有就是對自身的強引用,主要不能這樣 id __weak obj =[ [NSObject alloc] init],可以使用 id __weak obj1 = obj,c)__unsafe_unretained是不安全的所有權修飾符,它修飾的變數不屬於編譯器的記憶體管理,注意,ARC式的記憶體管理是編譯器的工作,d) ,__atuoreleasing 該修飾符下的變數等價於MRC下呼叫物件的autorelease方法,可以理解為在ARC下用@autoreleasepool替代NSAutoreleasePool類,用附有__atuoreleasing修飾符的變數替代autorelease方法,但是基本不常見這種修飾符;
7,在LLVM3.0以上,MRC和ARC下都可以使用@autorelease塊,因為autoreleasepool範圍以塊級源程式碼表示,提高了程式的可讀性,NSRunLoop均能隨時釋放註冊到autoreleasepool中的物件。
專欄
__strong 類似於C++ 中的 std::shared_ptr ,__weak 類似於C++的std::shared_ptr;
ARC下注意不能使用區域(NSZone),不要顯示呼叫dealloc;
8,通過__bridge可以將 id 和 void * 之間相互轉換,__bridge 轉換中還有兩種是__bridge_retained 和 __bridge_transfer,類似strong和weak;
9,CF 和 F框架下的轉換通常就是用的Toll-Free-Bridge,即免費橋;
10,不支援__weak 修飾符的類,其類宣告中附加了__attribute__ 這一屬性,在cocoa框架類中不支援__weak的極為罕見,還有一種情況下也不能使用__weak修飾符:-(BOOL)allowsWeakReference;返回為NO時,-(BOOL)retainWeakReference 返回為NO時;
11,講物件賦值給附有__autoreleasing修飾符的變數等同於MRC時呼叫物件的autorelease方法;
總結:這一篇章主要講述通過對比GNU來推測蘋果官方程式碼,對底層的程式碼分析來論述ARC和MRC的區別和為什麼使用這些修飾符,以及它們的好處,讀完,對這些新特性理解更加到位,的確受益匪淺。