Swift 與 Objective-C

Objective-C 的中括號語法,為什麼長這樣?(訊息傳送的秘密)

開始學習 iOS 後,第一次看到 Objective-C 程式碼時,很容易感到困惑。

閱讀 4 分鐘
Objective-C 的中括號語法,為什麼長這樣?(訊息傳送的秘密) 封面圖

開始學習 iOS 後,第一次看到 Objective-C 程式碼時,很容易感到困惑。

因為畫面上到處都是中括號 [ ]。看起來像是在呼叫函式,但外觀和其他語言差異太大了。

難免會留下「為什麼要設計成這樣?」的疑問。

先說結論,Objective-C 的中括號不是在「呼叫」函式,而是用來對物件「傳送訊息」的語法。只要掌握這個概念,原本陌生的中括號就會突然變得容易理解。

今天就從這個語法的根源開始,輕鬆拆解中括號為什麼長成這樣。😊


先掌握重點摘要

先整理一下這篇文章可以學到的內容。

  1. Objective-C 的中括號代表「對物件傳送訊息」
  2. 這套語法承襲自 Smalltalk 這個語言
  3. 只要了解 [객체 메서드] 這個結構,就能讀懂大部分內容
  4. 在內部會轉換成 objc_msgSend 函式後執行

只要理解這四點,今天的目標就全部達成了。


Objective-C 的訊息傳送語法,到底是什麼?

在其他語言中呼叫方法時,通常會這樣寫。

object.method()

但 Objective-C 會這樣寫。

// dog 對物件傳送 bark 訊息
[dog bark];

// count 將值一併作為引數傳送
[dog barkTimes:3];

外觀看起來陌生,但結構其實很簡單。

[받는객체 보낼메시지]

打開中括號,左邊放接收訊息的物件,右邊寫要傳送的訊息。

因此 [dog bark] 的意思就是「dog,請 bark」。

我把它理解成不是「呼叫函式」,而是「和物件說話」,就覺得容易多了。

有多個引數時,會像這樣延伸。

// 訊息名稱會 setName:age: 分成兩個部分
[person setName:@"哲秀" age:20];

可以把它想成每個冒號(:)後面都會接一個值。


中括號為什麼長這樣?

Objective-C 的中括號,是把 Smalltalk 的「訊息傳送」理念建立在 C 語言之上後留下的痕跡。

故事要回溯到 1980 年代初期。

Brad Cox 和 Tom Love 兩人共同建立了 Objective-C。

他們想在沿用當時流行的 C 語言之餘,再加入物件導向的概念。

那個物件導向的模範正是 Smalltalk。

Smalltalk 的核心理念是「一切都是物件,而物件彼此交換訊息」。

問題在於,必須在不破壞 C 語法的情況下加入訊息這個概念。

因此他們採用了 C 語法中幾乎不會用到的中括號,將其中定義為傳送訊息的空間。

如此一來,即使和既有的 C 程式碼混用,也能避免衝突並加以區分。

習慣「傳送」訊息的感覺後,中括號就會變得好用
習慣「傳送」訊息的感覺後,中括號就會變得好用

這個語言後來被 NeXT 採用,Apple 在 1996 年收購 NeXT 後,它成為 Mac 和 iPhone 開發的基礎。

我們熟悉的 Cocoa framework 也是由此發展而來。

一個陌生的中括號外觀,背後就承載了這麼悠久的歷史。


它和點號(.)語法有什麼不同?

現在看到 Objective-C 程式碼時,也經常會遇到點號(.)語法。

就像 dog.name 一樣。

因此很容易混淆,我把兩者的關係整理成表格。

分類 中括號語法 點號(.)語法
形式 [dog name] dog.name
意義 訊息傳送 屬性存取
出現時期 早期開始 Objective-C 2.0(2007 年)之後
內部運作 執行方法 實際上轉換成方法

有趣的是,點號語法在內部最後也會轉換成中括號訊息。

也就是說,dog.name 執行時會做和 [dog name] 相同的事。

點號語法只是一件讓程式碼看起來更容易閱讀的外衣。

所以我會建議初學者先理解,本質其實是中括號。


中括號裡實際上發生了什麼事?

讓我們再深入一點。

寫成 [dog bark] 時,編譯器會將它轉換成名為 objc_msgSend 的函式呼叫。

// 我們寫的程式碼
[dog bark];

// 在內部大致會轉換成這樣
objc_msgSend(dog, @selector(bark));

第一個位置放接收訊息的物件,第二個位置放要執行的方法名稱標籤(selector)。

外觀看起來不同,最後仍會匯聚到同一處
外觀看起來不同,最後仍會匯聚到同一處

這種方式很有彈性,因為可以在執行時決定要執行哪個方法。

因此 Objective-C 很擅長在執行期間變更或新增方法等動態功能。

中括號的外表之下,原來藏著這樣靈活的結構。

用上幾天後就會習慣的語法
用上幾天後就會習慣的語法

用 Q&A 做個總結

Q. 一定要背中括號嗎?

與其死背,只要掌握 [받는객체 보낼메시지] 這個結構,就能自然讀懂。

Q. 我現在使用 Swift,還需要知道嗎?

因為舊版函式庫或範例程式碼至今仍有許多使用 Objective-C 撰寫,能讀懂會很有幫助。

Q. 如果中括號裡又有中括號呢?

這代表外層會再次接收內層的結果。從內往外讀就能理解。


中括號看似只是奇怪的規則,但只要改成「和物件說話的語法」來思考,就會親切許多。

越是陌生的語法,了解它的根源後反而越能長久記住。

希望今天這篇文章能成為你更熟悉 Objective-C 的契機。為你加油!🙌

延伸閱讀