Objective-C 好用的 nil 和變數類型的思考

· 4min · chchwy

Objective-C 裡有個我非常喜愛的特性,就是對空物件 nil 調用方法不會出錯。 類似這樣:

NSArray* myArray = nil;
[myArray count]; // It's ok.

在許多主流語言裡,像是 C++,對 NULL 指標調用方法都是非常嚴重的錯誤,程式很可能會馬上崩潰。但是在 Objective-C 裡不同,對 nil 調用方法被視為稀鬆平常的事情,這個方法調用不會使程式崩潰,也不會拋出異常,程式單純的不做任何反應,船過水無痕地默默繼續運行。如果該方法有回傳值,就回傳 nil 或零。

空指標就不做事的原則,大多數時候也的確符合我們的期望,所以 Objective-C 程式裡不再需要處處塞滿 if ( ptr != NULL ) { ... } ,提心吊膽的提防遺漏的空指標造成程序崩潰,這個小差異大大提高了程序的穩定性。代碼也更簡短清晰了。

後來我發覺這個 NULL / nil 的小差異,其實沒那麼簡單,真正的問題是 NULL 指標本身的存在,就破壞了程式語言的類型保護網。

類型(Type)是編譯器給我們的一張保護網,string 類型的變數就只能呼叫 string 的方法,所有操作都由編譯器掛保證,不合法的方法調用,都在編譯期會被擋下來。

但是超越這張保護網的唯一例外就是 NULL 。當我們聲明一個 string* 類型的變數,其實真正的意思是該變數既可以存放 string 指標, 也可以 存放 NULL,對吧,兩者都合法,即使 NULL 完全不符合 string 類型的規範,一運行就會出錯,但是編譯器就是認可這個賦值,一聲不吭。

為了處理 NULL 特例,我們程式設計師只能辛勤的手動防範,每個引用到該變數的地方都區分有效變數或 NULL 兩種行為,最終導致程式碼裡頭無止盡的 NULL check ,因為我們不知道指標何時是有效的變數,何時是 NULL。也因此才有人提出 Null Object Pattern ,用團隊的編程規範去禁止 NULL 的使用,改以一個自己撰寫的不做事的空物件取代之。

所以 Objective-C 的 nil 並不單單只是一個不會程序崩潰的 NULL 指標而已,nil 滿足所有類型的規範,不再需要有特例需要防範,它其實等同了語言內建了所有類型的 NullObject 機制。

這篇文章大部分來自王垠博客的啟發。