這些年我們愛著的Objective-C

2020-12-11 雷鋒網

雷鋒網(公眾號:雷鋒網)按:本文作者唐天勇, iOS研發工程師。歡迎關注其知乎專欄「LeanCloud技術專欄」。

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 仍然會繼續發揮餘熱。

雷鋒網原創文章,未經授權禁止轉載。詳情見轉載須知。

相關焦點

  • 這些年我們愛著的 Objective-C
    出生Objective-C的誕生要追溯到1980年左右。那時Brad Cox和Tom Love兩位工程師還在ITT實驗室工作,他們意識到程序設計語言的抽象程度在軟體開發中扮演著重要角色。他們認為Smalltalk在這方面做得非常出色,但執行效率是瓶頸,並且實驗室裡大部分系統軟體都是用C語言實現的,因此他們希望把Smalltalk的好處帶到C語言裡。
  • Objective-C 與 Runtime:為什麼是這樣?
    1996年,Apple公司收購了NeXT公司,NeXTSTEP/OPENSTEP系統成為Apple新一代作業系統OS X的研發基礎。關於面向對象,把一定的算法(函數)和數據(變量)因某種內在的聯繫綁定在一起,形成最基本的程序結構單元,這些結構單元即是經常談及的對象,加上抽象二字,我們稱呼它為抽象對象,術語簡稱類;通過對變量的賦值(筆者認為不僅是變量,邏輯運算如閉包也是可以用於賦值)則會構成實體對象,術語簡稱對象(Objective-C一般也稱作實例)。
  • 10個Objective-C基礎面試題,iOS面試必備
    如果你準備去面試一個iOS開發崗位,那麼本文也許可以幫助你提前準備一些iOS面試題,這些面試題都是 Objective-C基礎面試題,一起來看看。1、#import和#include的區別,@class代表什麼?
  • Swift和Objective-C混編
    如果一個對象的方法或者屬性標記為@objc,那麼這些方法就變成可以動態查找的方法。因為Objective-C的方法是採用動態查找實現的,所以,只有這樣,這些方法才能提供給Objective-C使用。@objc後面有詳細敘述)例如,可以給AnyObject類型的常量或者變量賦值任意的對象,這個變量又可以再賦值一個不同類型的對象。
  • Objective-C的元組實現(JDTuple)
    我們在使用字典或數組時,總是需要判斷數組越界,判斷數據是否為空,判斷數據類型。有沒有覺得定義多參數函數的時候特別雜亂無章?有沒有覺得Swift的元組使用起來特別順心?這些問題其實都源於OC是一門比較古老的語言,早期設計時缺少一個足夠靈活,足夠好用的數據封裝工具。
  • Swift與Objective-C的兼容「黑魔法」:@objc和Dynamic
    我們通過使用2013年新引入的@import來引入module:@import MySwiftKit;之後就可以正常使用這個Swift寫的框架了。而Swift為了追求性能,如果沒有特殊需要的話,是不會在運行時再來決定這些的。也就是說,Swift類型的成員或者方法在編譯時就已經決定,而運行時便不再需要經過一次查找,而可以直接使用。顯而易見,這帶來的問題是如果我們要使用Objective-C的代碼或者特性來調用純Swift的類型時候,我們會因為找不到所需要的這些運行時信息,而導致失敗。
  • 「主觀的」是「objective」還是「subjective」?看了你就會了!
    He gave me some objective advice.他給了我一些客觀的建議。I find it difficult to be objective where he's concerned.只要涉及他,我就難以做到保持客觀。
  • 聊聊 Objective-C 循環引用的檢測
    結合以上兩點,我們可以嘗試分析以下 block 捕獲區域的變量布局情況 :NSObject *o1 = [NSObject new];__weak NSObject *o2 = o1;__block NSObject *o3 = o1;unsigned long long j = 4;int i = 3;char c = 'a'
  • 深入理解Objective-C的Runtime機制
    SELSEL是函數objc_msgSend第二個參數的數據類型,表示方法選擇器,按下面路徑打開objc.h文件:id接下來看objc_msgSend第一個參數的數據類型id,id是通用類型指針,能夠表示任何對象。按下面路徑打開objc.h文件:
  • Objective-C 裡的 eval
    2019 年的 RealWorldCTF 有一道叫「德州計算器」的題目。
  • 語法淺析--從C語言到Objective-C
    深層次的語言設計初衷就不再探討了,這些理由基本都囊括在自己的語言特性裡,有人說想深學一門語言,就要學它的歷史。從某個角度來說,這也是為了更方便我們去理解,設計語言的人,想要用這個語言來表達什麼而已。 順便討論下日常語言和計算機語言的區別,對於日常語言和來說計算機語言,最大的區別在於受體。
  • Objective-C
    在2004年,在 Cocoa 編程環境中,所有 Mac OS X 類別和函式均有「NS」作為前綴,例如 NSObject 或 NSButton 來清楚分辨它們屬於 Mac OS X 核心;使用「NS」是由於這些類別的名稱在 NeXTSTEP 開發時定下。雖然 Objective-C 是 C 的超集,但它也不視 C 的基本型別為第一級的對象。
  • 關於Objective C編碼規範,這些你一定要會
    例外:這不應用在生成文檔的注釋Apple命名規則儘可能堅持,特別是與這些相關的memory management rules。長的,描述性的方法和變量命名是好的。應該:objc NSInteger arrayCount = [self.array count]; view.backgroundColor = [UIColor orangeColor]; [UIApplication sharedApplication].delegate;
  • Objective-C 和 Swift 面試題
    看名字我們可以知道,它與 C 語言有千絲萬縷的聯繫,事實上也確實如此:Objective-C 是 C 語言的超集,它在 C 語言主體上加上了面向對象的特性。這是為了 App 開發的方便,同時也兼顧了語言的整體性能。2014年來,Swift 橫空出世,功能不斷完善,逐漸成為 Apple 全力主推的官方程式語言。自發布以來,Swift 已經歷經4個版本的迭代。
  • J2ObjC 1.0 發布,將 Java 轉換為 Objective-C
    我們在2012年發布了J2ObjC的alpha版本,並公布了部分實現細節。J2ObjC的逐漸發展到穩定版1.0版本,並應用於開發Inbox、Calendar、Docs、Sheets、Slides和Google My Business等Google產品的iOS版本。
  • Java受Objective-C影響很大 而不是C++
    通常人們會認為Java是學Modula-3和C++,甚至還有人說Java是模仿NewtonScript,這些都是胡說八道和謠傳。Java語法可能與C++類似,但作為語言來講,其與C++並無相似性。Java的核心語法是動態綁定以及主要使用單繼承、類對象以及可擴展的運行時系統。而C++和Modula-3則遠離這種任何面向對象語言都所具備的特性。
  • 輕鬆學習之 Objective-C消息轉發 乾貨
    具體實現內容就為上面的void run 這個c函數。)當外部調用[p run]時,由於我們沒有實現run對應的方法,那麼系統會調用resolveInstanceMethod讓你去做一些其他操作。(當然,你也可以不做操作,只是在這個例子中,我為run方法動態增加了實現。)
  • Swift學習: 從Objective-C到Swift
    而在Swift的世界中,我們通過其他方式來聲明這些property的性質。需要注意的幾點:strong: 在Swift中是默認的weak: 通過weak關鍵詞申明 weak var delegate: UITextFieldDelegate?
  • Objective-C中一種消息處理方法performSelector: withObject:
    Objective-C中調用函數的方法是「消息傳遞」,這個和普通的函數調用的區別是,你可以隨時對一個對象傳遞任何消息,而不需要在編譯的時候聲明這些方法
  • Objective-C:神在細節之中
    而在日常的編程中,我們除了要寫代碼,還需要去閱讀別人的代碼,熟悉過往的業務邏輯。不知,你可曾發過牢騷:這代碼怎麼能這麼寫呢?有些時候我們的代碼,也會被別人去讀,不知你可曾想過,當別人讀到你的代碼的時候會作何評價。誠然,「讓代碼能夠工作」是做為開發者的頭等大事。但是,代碼的可維護性卻是更加影響深遠的一件事情。