Swift 的存取控制機制 (Access Control)

WuFeng Chiang
6 min readDec 11, 2019

--

由於本文屬於「備忘」性質,如果在閱讀過程中有遇到理解困難的話,除了可能是筆者寫作不周外,也可以參考 Swift 文件:

https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html

存取等級 (Access Levels)

Swift 的五種存取等級中,對於開發 App 的人,大概只會用到三種,也就是 internal (預設等級)、fileprivate、private;另外兩種的 open、public 則是開發「跨模組」(例如開發一個框架、SDK 給別的開發者用) 應用時才需要考量。

沒有意義也不會出錯的 public 特性

範例一

範例一中,編譯器、Xcode 不會發出警告或錯誤,但其實 title 屬性的 public 在此例是沒效用的。因為:Movie 本身是 internal,也就是 Movie 類別本身並不允許外部模組存取,自然它的 title 特性就更是。

對於 public 的定義,Swift 和其他幾種更傳統的主流程式語言不同,對於以往學習、使用且熟悉其它們的人,在撰寫 Swift 時要留意這點。

子類別不可以比父類別開放

也就是不能透過繼承的方式,讓子類別對外曝露原本父類別不願對外透露的訊息。

範例二

範例二中,C1 類別無法在模組外被存取,但 C2 試圖開放。不行。

範例三

範例三中,C1 只想對同一檔案中的其他類別開發存取,但是 C2 試圖開放給其他檔案中的類別也能存取。也不行。

同樣的例子的話,Java 則沒有這個限制。在 Java 裡,若父類別不使用 final 禁止繼承,或是不把所有不想對外公開的 attributes / methods 都設定成 private 的話,子類別都可以自行曝露出去。

如果是「父類別是別人提供的」、「子類別是自己寫的」,那當然感覺「很自由」;若「父類別是自己提供的」、「子類別是別人寫的」,感覺就有點「危險/意外」了。

Java, 父類別。
Java, 子類別

也就是在 Java …防兒子和防外人沒兩樣…。

子類別裡的成員可以比父類別開放,而且不可以更封閉

範例四

這點符合里氏替換原則 (Liskov Substitution principle)。用擬人白話來說的話,就是「父親對外的承諾,做兒子的只能遵守或投報更多」。對於父類別的 client 來說,在 runtime 透過注入把 instance 換成子類別的物件時,要能 work。

範例五

在 Java 中,對於 method 的 override 行為也是這樣,但是對 attribute 卻沒有限制:子類別的同名屬性可以比父類別的更開放。不過這是因為:在 Java 裡,attribute 沒有 override 的行為。事實上感覺也沒必要:若值相同就用父類別物件有的,若值不同就子類別物件自己存一份。

但是在 Swift 中,property 的能力比 Java 的 attribute 複雜:property 有觀察者 (Observers) 機制、computed (get/set) 機制,而子類別的 property 可以在這些行為上與父類別定義的 property 不同,這也是上圖範例五中為何要在 prop 特性裡加上 didSet{} 宣告的原因 (不加上這個看似無意義的程式碼的話,Xcode 是會警告出錯的哦。)。所以在 Swift 中 property 在繼承關係上有 override 的機制是有意義的。

Property 的讀寫存取可以不同等級

Swift 裡 property 可以在 get 和 set 兩者分別宣告不同的存取等級。如下:

範例六

Sub 子類別對 Super 父類別的 prop1 特性進行 override,意圖在 willSet 時觸發不同於 Super 父類別 prop1 特性不同的行為。這裡的 override 還開放了 Sub 子類別 prop1 的 get 等級成為 internal,但是保持了 set 等級為 fileprivate。

Property! 來和 Java 做個比較

Java 的「成員變數 (data member)」稱之為「attribute」,而以 private 宣告、並加上其對應的 get 和 set 方法後,三者 (attribute + get 方法+ set 方法) 才滿足 property 的定義。(property 可以用來表現 attribute,但是它不是 attribute)

參考:https://www.uml-diagrams.org/property.html

在 Swift 中,由於語法支援已經包括 get/set (computed property)、observers (可以針對 willSet / didSet 的發生時機進行控制)、lazy loading、property wrapper…等機制,所以符合完整的 property 概念,當然在使用上就更為精細了。

--

--