這些年,我們愛著的 Objective-C⋯⋯

Objective-C 是開發 OS X 和 iOS 應用的標準語言。即便是天天跟它打交道的開發者,有些也會誤以為 Objective-C 就是 Apple 公司創建出來的語言,但實際上它並不是 Apple 的親骨肉,而是從別人家過繼過來的孩子⋯⋯
評論
評論

原文刊載於 雷鋒網 ,Inside 獲授權刊登。

Objective-C 是開發 OS X 和 iOS 應用的標準語言。即便是天天跟它打交道的開發者,有些也會誤以為 Objective-C 就是 Apple 公司創建出來的語言,但實際上它並不是 Apple 的親骨肉,而是從別人家過繼過來的孩子。

程式設計語言是一個規範,它可以有許多種應用。在歷史的漫漫長河中也出現過其他 Objective-C 應用,下面我會主要以 Apple 的 Objective-C 角度來論述。

出生

Objective-C 的誕生要追溯到 1980 年左右。那時 Brad Cox 和 Tom Love 兩位工程師還在 ITT 實驗室工作,他們意識到程式設計語言的抽象程度在軟體開發中扮演著重要角色。他們認為 Smalltalk 在這方面做得非常出色,但執行效率是瓶頸,並且實驗室裡大部分系統軟體都是用 C 語言完成的,因此他們希望把 Smalltalk 的好處帶到 C 語言裡。Objective-C 就是在這樣的背景下誕生了。

成長

1983 年,Brad Cox 和 Tom Love 成立了 Stepstone 公司,並發表了第一個 Objective-C 應用。據 Brad Cox 回憶,最初的應用就是個簡單的預處理器。他們使用 sed 和 awk 等工具把 Objective-C 直接翻譯為 C 語言。可 C 語言並不原生支援 Objective-C 的動態語義,例如 dynamic dispatch。動態語義是透過一個 library 來實現的,這個 library 逐漸發展成了現在的 runtime library(簡稱 runtime)。

後來 Stepstone 被 Steve Jobs 領導的 NeXT 收購,Objective-C 由此迎來它的首次發展。不過 NeXT 並沒有在語言中引入新特性,而是對 runtime 做了一些優化,即 NeXT runtime。

Apple 收購了賈伯斯的 NeXT 後,就把 NeXT 當時在使用的 Objective-C 直接繼承了過來,即所謂的「Objective-C 1.0」。後來 Apple 在語言中增加了一些新特性,例如屬性、fast enumeration、垃圾回收(後來被 ARC 取代)。不久前 Apple 為 Objective-C 引入了輕量級泛型。這樣的改進可謂進步巨大,runtime 也被完全重寫並開源了出來。 Apple 將這一新的應用稱為「Objective-C 2.0」,也就是我們現在看到的這個樣子。

Apple 對語言的持續改進讓開發者歡欣鼓舞。Objective-C 2.0 也在 OS X 和 iOS 應用開發的浪潮中站穩了腳跟。作為語言的使用者,我對 Objective-C 語言是愛恨交加。一方面,小巧的語言結構、動態的本質讓開發變得多姿多彩,特有的方括號也讓程式碼添了幾分味道;另一方面,從現代的眼光來看,語言缺乏一些高級特徵,比如命名空間 namespace、完整的泛型支援等。雖然仍不完美,但承擔起 Apple 應用開發的重擔,它做到了。

性格

早期的 Objective-C 只支援手動記憶體管理,開發者必須仔細去跟蹤每個對象的生命周期,猶如做針線活一般,步步小心謹慎,否則就會扎破手指。而同時期的許多語言都已開始引入了更高級的記憶體管理,例如垃圾回收,Java 就是其中的典型代表。雖然手動記憶體管理在執行效率上有優勢,但同時也帶來了額外的心智負擔。好在後來引入了自動引用計數 ARC。雖然它的背後仍然是引用計數,但大多數時候不需要開發者跟蹤對象的生命周期,而是交由編譯器和 runtime 來做。開發者只需要注意一些特殊情況即可,例如循環引用。

Objective-C 不支援 namespace,也就意味著它不支援嵌套類,所有符號會在編譯階段連接到一個全局的 namespace 下。如果連接時發現了重複的符號,編譯就會失敗。為何 Apple 遲遲不在 Objective-C 中引入 namespace 呢?其實 Apple 曾多次考慮過這個問題,但發現這是個大坑,填起來困難重重。

困難之一是與 C 和 C++ 之間的協調。由於 Apple 允許 Objective-C 和 C++ 混合編程(Objective-C++),這就要求 Objective-C 的 namespace 與 C++ 的保持相容,然而這是不可能的。另一種選擇是只對 Objective-C 語言實現 namespace,不過也有問題。因為 Objective-C 語言是 C 語言的超集,C 語言沒有 namespace 的概念,其結果只能對 Objective-C 的類實現 namespace。但是 Objective-C 中的那些方法又會最終編譯成 C 語言的函數,只能對方法做 name mangling,這又會導致 ABI 不相容。目前 Apple 尚未找到能完美解決這一問題的方案,Objective-C 也只能先感慨著與 namespace 的緣分未到了。

Objective-C 早期不支援匿名函數或者閉包,但隨著社群的呼聲越來越高,Apple 最終為 Objective-C 增加了這一特性。不過有趣的是,Apple 是直接在 C 語言中實現的,稱之為 block。Objective-C 擴展了 block,以適應 Objective-C 的記憶體管理。

Objective-C 1.0 不支援泛型。直到 Objective-C 2.0 才引入了輕量級泛型。注意,之所以輕量級是因為它完全由編譯器實現,沒有 runtime 參與,泛型訊息在程式碼生成階段就被丟棄了。可能 Apple 也不希望在 runtime 中引入額外的代價吧。

社群

隨著 OS X 和 iOS 開發生態的繁榮。越來越多的開發者投入到 OS X 和 iOS 應用開發中來,社群湧現出許多優秀的第三方庫。

第三方庫不斷積累,包括依賴管理亟待解決。可 Apple 並沒有官方的解決方案。既然沒有官方的,那就自己動手做一個吧,CocoaPods 就是這樣問世的。不過由於 Xcode 的封閉,CocoaPods 的開發過程也非常艱難,經常因 Xcode 的升級而出現不相容的狀況。但 CocoaPods 最終克服了重重障礙,成為管理依賴的首選。

社群的不斷壯大讓 Objective-C 長期占據著 TIOBE 排行榜前 10 的位子,所以這門古老的語言在 21 世紀依然能綻放耀眼的光芒。

未來

2014 年一聲炮響,Swift 橫空出世,Apple 不遺餘力地向世界宣告著這個親骨肉的到來,而有關繼子 Objective-C 該何去何從的討論也愈演愈烈。誠然,Swif 解決了 Objective-C 的許多痛點,例如支援 namespace、運算符重載、更輕鬆的記憶體管理等。我想,Objective-C 不會在短時間內離我們遠去,因為社區中還有大量的 codebase 是用 Objective-C 寫的。並且,Objective-C 與 C / C++ 語言更親近,如果專案中需要和大量 C / C++交互使用,Objective-C 仍然會繼續發揮餘熱。