Swift

2023 年 10 月 3 日

Swift 語法中的五個存取控制

已複製到剪貼板


Swift 的 Access Control 主要靠:open、public、internal、fileprivate、private 這五個關鍵字來管理程式的存取權,或者是說可見度。

Swift Access Control

Swift 的五個存取控制關鍵字

Swift 提供了以下五種不同程度的程式存取權,由大到小:

open 和 public

open 擁有最高的權限,而 public 第二高,它們可以在任何地方被存取,不管是在同一個專案裡,或是被 import 到其他專案時,都能被存取。通常我們會將 Framework 要開出去的介面設成 open 或是 public。

open 和 public 的差異

open 只能作用在 class 與 class 裡面的成員,雖然不像 public 沒有限制使用對象,但卻多了一個特性,open 讓 class 可以在其他專案裡被繼承,而 public 則無法讓 class 在不同專案裡被繼承。

internal

internal 可以在同個專案裡的任何地方存取,但如果是在其他專案,就無法看到 internal。這是 Swift 的預設存取權,當我們沒特別寫存取控制關鍵字時,預設就是 internal,而不是 public!

fileprivate

fileprivate 就如其名,只能在同一份檔案內使用。不過如果以 class 舉例(或 struct),通常一個檔案裡只會有一個 class,而如果有多個時,被 fileprivate 指定的 class 只能被這份檔案裡的其他 class 使用。

private

private 的權限最低,被 private 指定的東西,如 class 裡面的一個 private var,只能在自己內部使用,外面的任何地方都看不到也無法存取此 private。

Swift 存取控制在 Unit Test 的特例

在 Swift 中,預設的存取控制就是 internal,而 internal 是無法被其他專案看到的,但是在 Unit Test 裡例外,通常我們在寫測試時,除了 import xctest,還會在要測試的 module 前加上 @testable,這會讓 Unit Test 有能力看到被測試 module 裡的 internal。

Swift 存取控制的向下作用

當我們對一個 type 指定了某個存取權,這個存取權會連帶的影響 type 裡面的成員。例如,有一個 private class,那它裡面的 var 和 func 預設都會是 private。

注意

不過這裡有個例外,public 並不會連帶的影響成員為 public,預設仍然是 internal,而如果希望裡面的成員也能是 public 的話,則要自己特地的加上關鍵字 public。

以下舉一些實例:

public class SomePublicClass {                   // public
    public var somePublicProperty = 0            // public
    var someInternalProperty = 0                 // internal
    fileprivate func someFilePrivateMethod() {}  // fileprivate
    private func somePrivateMethod() {}          // private
}

class SomeInternalClass {                        // internal
    var someInternalProperty = 0                 // internal
    fileprivate func someFilePrivateMethod() {}  // fileprivate
    private func somePrivateMethod() {}          // private
}

fileprivate class SomeFilePrivateClass {         // fileprivate
    func someFilePrivateMethod() {}              // fileprivate
    private func somePrivateMethod() {}          // private
}

private class SomePrivateClass {                 // private
    func somePrivateMethod() {}                  // private
}

Tuple 的存取控制

因為一個 Tuple 會由兩個型別所組成,所以 Tuple 的存取控制便是由這兩個型別的存取權所決定,我們無法直接指定 Tuple 的存取權,Swift 會在這兩個之間選最嚴格的存取權來當作 Tuple 的存取權。舉例來說,一個 Tuple 是由 internal 和 private 所組成,那這個 Tuple 的存取權就是 private。

Function 的存取控制

Function 的存取控制會受到它的參數與回傳型別所影響,雖然我們可以指定一個 Function 的存取權,但並不能大於參數與回傳型別所算出來的存取權。

舉個例子:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {}

這個 Function 回傳一個 Tuple,而這個 Tuple 是由 internal 和 private 所組成的,所以回傳的存取權是 private,那我們的 Function 最多就只能設成 private,並不能指定成權限大於 private 的其他如 public 或是 internal。

Getter 與 Setter 的存取控制

Getter 與 Setter 的存取控制預設都會和常數或變數本身的存取控制相同,不過我們可以透過 fileprivate(set)private(set)internal(set) 來讓 Setter 的存取控制低於 Getter,因為我們可能希望一個變數是可以被外部讀取的,但它只能被內部設定,這算是 Swift 蠻方便的一個語法,因為像在別的語言,如 Java 要做到這件事,只能把變數設為 private,並額外多寫兩個 Get 與 Set 的 public func,來讓外部透過 func 來存取設定此變數。

這裡舉個實際範例:

struct TrackedString {
    private(set) var numberOfEdits = 0 // 外部可讀,但只能在內部設定
    var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
}

不過這裡比較特別的是,因為 Swift 的存取控制預設為 internal,所以當我們寫 private(set) 時,Getter 是 internal,而我們也可以同時設定 Getter 與 Setter,比如將 Getter 改為 public:

struct TrackedString {
    public private(set) var numberOfEdits = 0 // 外部可讀,但只能在內部設定
    var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
}

順序為:第一個是 Getter、第二個是 Setter。

分享文章

已複製到剪貼板

主題文章

查看 Swift

超級感謝

關於 XcodeProject

XcodeProject 創立於 2023,致力於協助開發者探索 Apple 的創新世界,學習在 iOS、iPadOS、macOS、tvOS、visionOS 與 watchOS 上開發 App,發現眾多技術與框架,讓開發者獲得更多能力。


Contacts

Ricky Chuang

XcodeProject

RickyChuang.xcodeproj@gmail.com

XcodeProject 聯絡

contact.xcodeproj@gmail.com

最新文章