可空性與 Objective-C

戴倉薯發表於2015-05-08

可空性與 Objective-C

Swift 的一大優點是它能與 Objective-C 程式碼混編,不論是由 Objective-C 寫成的庫還是你的應用中的 Objective-C 程式碼都可以暢通的與 Swift 互動。然而,在 Swift 裡可選(optional)的引用與非可選(non-optional)的引用涇渭分明,例如NSView相對於NSView?,在 Objective-C 裡這兩種型別都對應NSView *。因為 Swift 的編譯器判斷不出某個NSView *是否是可選值,因此轉換為 Swift 的型別就是隱式解包可選型別NSView!

在之前版本的 Xcode 中,有些蘋果官方的框架經過了專門的稽核,來讓 API 正確反映在 Swift 中是否是可選型別。為了支援在你自己的程式碼裡也能實現這種功能,Xcode 6.3 推出了一項新的 Objective-C 語言特性:nullability annotations

核心:nullable 與 nonnull

這項新特性的核心是兩個新的型別註解:__nullable__nonnull。正如你所想,標記了__nullable的指標的值有可能為NULLnil,而__nonnull的指標則不可能。如果違反這些規則,編譯器會發出警告。

幾乎所有能用 C 傳統的const關鍵字的地方都能用__nullable__nonnull,當然只能用在指標型別上。不過,一般情況下有一個更好的方法來使用這兩個註解:用在方法宣告裡時可以不用加下劃線,直接寫在左括號後面,只需後面的型別是普通物件或 block 的指標。

對於屬性,也可以不用加下劃線,寫在屬性特性列表裡即可。

不加下劃線的形式比加下劃線的要好一些,不過你仍然需要在標頭檔案的每個型別前都加一遍註解。想要輕鬆一些同時讓標頭檔案更簡潔,你可以使用稽核區(audited region)。

稽核區

為了循序漸進地採用這些新註解,你可以給 Objective-C 標頭檔案中的一個區塊加上“經過非空稽核”的標記。在這個區塊內部,每個普通的指標型別都會預設為非空。這能讓上文的例子大大簡化:

出於安全考慮,這條規則有幾個例外:

  • typedef 型別一般沒有內在的可空性——它們要麼為空,要麼非空,根據具體的取值而定。因此,即使在稽核區內,typedef 型別也不會預設為nonnull
  • 複合指標型別,如id *,必須顯式使用註解。例如,指明一個非空的指標指向可空的物件引用,要用__nullable id * __nonnull
  • 一個比較特殊的型別NSError **一般用來在方法引數中返回錯誤資訊,因此它總是預設為可空的指標指向可空的NSError引用。

這方面的更多資訊可以參考Error Handling Programming Guide

相容性

如果現有的程式碼用到了你的 Objective-C 框架,卻與你指定的非空性規則相互牴觸怎麼辦?就這樣一下子改變你的型別真的安全嗎?放心,是安全的。

  • 現有的編譯好的程式碼如果用到了你的框架,仍然能正常執行,也就是說 ABI 不會變。這也意味著這些現有的程式碼在執行時不會捕捉到傳遞 nil 的錯誤。
  • 現有的原始碼如果用到了你的框架,用新的 Swift 編譯器編譯時,會得到不安全用法的警告。
  • nonnull 不會影響編譯優化。尤其是,你仍然可以在執行時檢查標註為nonnull的引數,看它實際上是不是nil。這對於向前相容可能是必要的。

總體來說,基本上你應該像看待 assertion 或 exception 一樣看待nullablenonnull:違反這些規則是程式設計師的錯誤。尤其是,返回值是你能控制的,所以一定不要對非空的返回型別返回nil,除非是為了向前相容。

回到 Swift

現在我們已經給 Objective-C 的標頭檔案加了可空性註解,來看看在 Swift 裡該怎麼用吧:

在給 Objective-C 程式碼加註解之前:

加了註解之後:

Swift 的程式碼更清晰了。只是一個微小的改變,卻能讓你的框架更易於使用。

C 和 Objective-C 的可空性註解在 Xcode 6.3 及以後版本可用。檢視更多資訊,請參考Xcode 6.3 Release Notes

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式

可空性與 Objective-C 可空性與 Objective-C

相關文章