主題01:熟悉 Objective-C 的根源
主題11:理解 objc_msgSend 的角色
主題29:理解參考計數
整理
主題01
打開 Objective-C 的身份證一看,它出生自1983年,是受Smalltalk語言啟發並擴充標準 ANSI C語言而成的物件導向程式語言。它是商標權是 Apple 的,Apple 也是它的主要開發者,而 Apple 將它作為開發 OS X 與 iOS 相關應用程式的主要程式語言。
Apple 是 Objective-C 的實作者,不過設計者另有其人。查查維基百科後,發現設計者有兩位,生平事績不多的樣子,而且和 Apple 的淵源不太明顯。不曉得那兩位設計者現在會不會覺得受寵若驚,或是因此名利雙收?這些猜想都未經查證了。
Objective-C 的長相挺特別,有很多的「方括號」,方法名稱的長度被人誤稱為「冗長」。冗長的說法大概是對比於成名早於它的前輩程式碼語言,因為那些前輩很愛用縮寫,那些前輩的程式員簡直就當自己是魔法使,程式語言就是他們的咒語一樣難解、難記但是能讓電腦很酷地工作。比較起來,Objective-C冗長的方法名稱,造就了較佳的程式碼可讀性。假若大家同意:自己每天讀程式碼的時間都比寫程式碼的時間長,大概就會同意 Objective-C 的特性之一是「可讀性高」而不是「冗長」。
Objective-C 是怎麼把物件導向的特性加入至 C 語言中的?答案是:將 Smalltalk 的訊息傳遞機制導入 C 語言。這個答案成為主題01的重點。
messaging 架構和 function calling 架構差在哪?很簡單講就是:前者是在 runtime 階段決定執行內容,而後者則是在 compiler 階段就先決定好。由於一般高階程式語言都是:compile 階段
-> linking 階段 -> runtime 階段的執行順序,所以可知:messaging 架構花在最後的 runtime 階段的執行成本高,而 function calling 架構則在 compile 階段就決定好,這樣每次執行的執行效能應該會比較好。
從可程式化的靈活性來看,由於 messaging 架構是到 runtime 階段才決定實際執行的內容,所以比這一點的話, messaging 架構的靈活度又高於 function calling 架構。也就是說,messaging 架構動態支援比較好。
Objective-C 的 messaging 架構主要是由 Objective-C 的 Runtime Library 實作。身為一般的開發者其實是用不太到 Runtime Library的,除非你正在為 Objective-C 實作 debugger 或跨語言的橋接。這是官方說法。
主題01的第二個重點,則是提到了和 C 語法在記憶體設計相關的機制。簡單講,程式語言在執行期會在主記憶體內規劃出三個區段用來運作語言本身。三個區段分別為 global、stack 及 heap。程序導向語言中的 global 變數或是物件導向語言中的類別屬性會放在 global,而生命週期有明確規範的資料結構會存在 stack,而物件會存放於 heap。
為什麼物件存放在 heap?因為它的生命週期無法用 scope 規範:物件可以在整個 App 裡被各種其他的物件參考,只有在它本身成為孤島或某一孤島的一員時,生命週期才會準備被結束 (還不是立即),所以不符合能放在 stack 的原則。這個其實想想 stack 的工作原理就能明白。
圖片來源:http://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap
主題11
這個主題延伸了主題01中的 messaging 機制。
很簡單地講,就是當有一個式子如下:
id value = [someObject messageName: parameter];
id value = object_msgSend(someObject, @selector(messageName:), parameter);
在 runtime 階段,Runtime Library 負責在 someObject 中找尋 messageName:,若找不到就往父物件去找,都找不到時,會引發 message forwarding 機制。找尋 messageName: 的過程會記錄在 someObject 對應的類別有含有的一份 fast map中做為快取,所以 messaging 其實也不會比 function calling 慢多少。
有關於 object_msgSend 相關函式還有很多相關的方法,要進一步鑽研,就得去看 Runtime Library了。
主題29
這個主題延伸自主題01中提到的:物件存放在 heap 區的觀念。
由於物件的生命週期是由還有多少其他的物件擁有/需要他來決定的,而不是由可結構化的程式區塊/scope決定,所以物件被放在 heap 區,而非 stack。而這個主題要回答的問題是:什麼時候在 heap 中的物件該是生?何時該被銷毀?
很簡單的說法是:當有人需要物件,它就因應而生;當再也沒有別的物件需要它時,它就等著被系統回收。(嚴格講起來要提一下 run loop,不過…先這樣理解吧)
Objective-C 用於管理 heap 中物件的方式,叫「參考計數」。想像每個物件身上掛著這個計數器,它一出生時,大抵上計數器上的值為「1」:因為產生該物件的物件將擁有該物件。
所謂的產生,大概說來是指使用 alloc 及 init 相關訊息來新創一個物件的行為。
然後當有別的物件也需要「他」時,可以向「他」傳遞 retain 訊息;反之,可以傳遞 release 訊息來表示不再需要他。
一旦「他」這個物件身上掛的計數器為0,他就會被系統標示成非使用中,然後等著被消滅。
這個機制有個簡單的原則:誰產生/擁有了某物件,就要負責釋放他。這個講來簡單,但是還是挺需要練習和思索的。
此外,還有以下注意事項:
- 一旦某物件的計數歸零,就會被標示為非使用中,但是其參考變數仍指向它,很可能造成不可預期又難以尋找的臭蟲。所以若確定 release 後計數應該歸零,最後在下一行緊接著把參考變數指向 nil 值。
- 設定 property 特性時,使用 retain / strong 的話,在 setter 執行時,其內容相等於:保留傳入參數、釋放原屬性,然後把傳入參數指派給原屬性。這個可延伸至主題06。
- 這個主題中,延伸了 autorelease 這個機制的討論。要很簡單的說,就是把 release 的時機延後到出了 autorelease pool 的範圍再說。這個可延伸至主題34。
- 這個主題中,還提到了物件的保留循環 (retain cycle)。有個更傳神的說法是「孤島效應 (island of isolation)」。簡單地說,就是有一組物件互相擁有/需要,但是他們和應用程式中的其他物件都沒有掛勾到,而自成了一個小圈圈。應用程式用不到它們,而它們的計數值也都不為零 (因為小圈圈內的物件互相需要),所以也不會被系統偵測為應該消毀的一群佔著記憶體不工作的物件小團體。這個可延伸至主題33討論。
延伸閱讀
沒有留言:
張貼留言