Swift 訪問控制

訪問控制可以限定其他原始檔或模組中程式碼對你程式碼的訪問級別。

你可以明確地給單個型別(類、結構體、列舉)設定訪問級別,也可以給這些型別的屬性、函式、初始化方法、基本型別、下標索引等設定訪問級別。

協議也可以被限定在一定的範圍內使用,包括協議裡的全域性常量、變數和函式。

訪問控制基於模組與原始檔。

模組指的是以獨立單元構建和釋出的 Framework 或 Application。在 Swift 中的一個模組可以使用 import 關鍵字引入另外一個模組。

原始檔是單個原始碼檔案,它通常屬於一個模組, 原始檔可以包含多個類和函式 的定義。

Swift 為程式碼中的實體提供了四種不同的訪問級別:public、internal、fileprivate、private。

訪問級別 定義
public 可以訪問自己模組中原始檔裡的任何實體,別人也可以通過引入該模組來訪問原始檔裡的所有實體。
internal 可以訪問自己模組中原始檔裡的任何實體,但是別人不能訪問該模組中原始檔裡的實體。
fileprivate 檔案內私有,只能在當前原始檔中使用。
private 只能在類中訪問,離開了這個類或者結構體的作用域外面就無法訪問。

public 為最高階訪問級別,private 為最低階訪問級別。

語法

通過修飾符public、internal、fileprivate、private來宣告實體的訪問級別:

例項

public class SomePublicClass {} internal class SomeInternalClass {} fileprivate class SomeFilePrivateClass {} private class SomePrivateClass {} public var somePublicVariable = 0 internal let someInternalConstant = 0 fileprivate func someFilePrivateFunction() {} private func somePrivateFunction() {}

除非有特殊的說明,否則實體都使用預設的訪問級別 internal。

未指定訪問級別預設為 internal

class SomeInternalClass {} // 訪問級別為 internal let someInternalConstant = 0 // 訪問級別為 internal

函式型別訪問許可權

函式的訪問級別需要根據該函式的引數型別和返回型別的訪問級別得出。

下面的例子定義了一個名為someFunction全域性函式,並且沒有明確地申明其訪問級別。

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函式實現
}

函式中其中一個類 SomeInternalClass 的訪問級別是 internal,另一個 SomePrivateClass 的訪問級別是 private。所以根據元組訪問級別的原則,該元組的訪問級別是 private(元組的訪問級別與元組中訪問級別最低的型別一致)。

因為該函式返回型別的訪問級別是 private,所以你必須使用 private 修飾符,明確的宣告該函式:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函式實現
}

將該函式申明為 public 或 internal,或者使用預設的訪問級別 internal 都是錯誤的,因為如果這樣你就無法訪問 private 級別的返回值。


列舉型別訪問許可權

列舉中成員的訪問級別繼承自該列舉,你不能為列舉中的成員單獨申明不同的訪問級別。

例項

比如下面的例子,列舉 Student 被明確的申明為 public 級別,那麼它的成員 Name,Mark 的訪問級別同樣也是 public:

例項

public enum Student { case Name(String) case Mark(Int,Int,Int) } var studDetails = Student.Name("Swift") var studMarks = Student.Mark(98,97,95) switch studMarks { case .Name(let studName): print("學生名: \(studName).") case .Mark(let Mark1, let Mark2, let Mark3): print("學生成績: \(Mark1),\(Mark2),\(Mark3)") }

以上程式執行輸出結果為:

學生成績: 98,97,95

子類訪問許可權

子類的訪問級別不得高於父類的訪問級別。比如說,父類的訪問級別是internal,子類的訪問級別就不能申明為public。

例項

public class SuperClass { fileprivate func show() { print("超類") } } // 訪問級別不能高於超類 internal > public internal class SubClass: SuperClass { override internal func show() { print("子類") } } let sup = SuperClass() sup.show() let sub = SubClass() sub.show()

以上程式執行輸出結果為:

超類
子類

常量、變數、屬性、下標訪問許可權

常量、變數、屬性不能擁有比它們的型別更高的訪問級別。

比如說,你定義一個public級別的屬性,但是它的型別是private級別的,這是編譯器所不允許的。

同樣,下標也不能擁有比索引型別或返回型別更高的訪問級別。

如果常量、變數、屬性、下標索引的定義型別是private級別的,那麼它們必須要明確的申明訪問級別為private:

private var privateInstance = SomePrivateClass()

Getter 和 Setter訪問許可權

常量、變數、屬性、下標索引的Getters和Setters的訪問級別繼承自它們所屬成員的訪問級別。

Setter的訪問級別可以低於對應的Getter的訪問級別,這樣就可以控制變數、屬性或下標索引的讀寫許可權。

例項

class Samplepgm { fileprivate var counter: Int = 0{ willSet(newTotal){ print("計數器: \(newTotal)") } didSet{ if counter > oldValue { print("新增加數量 \(counter - oldValue)") } } } } let NewCounter = Samplepgm() NewCounter.counter = 100 NewCounter.counter = 800

counter 的訪問級別為 fileprivate,在檔案內可以訪問。

以上程式執行輸出結果為:

計數器: 100
新增加數量 100
計數器: 800
新增加數量 700

構造器和預設構造器訪問許可權

初始化

我們可以給自定義的初始化方法申明訪問級別,但是要不高於它所屬類的訪問級別。但必要構造器例外,它的訪問級別必須和所屬類的訪問級別相同。

如同函式或方法引數,初始化方法引數的訪問級別也不能低於初始化方法的訪問級別。

預設初始化方法

Swift為結構體、類都提供了一個預設的無參初始化方法,用於給它們的所有屬性提供賦值操作,但不會給出具體值。

預設初始化方法的訪問級別與所屬型別的訪問級別相同。

例項

在每個子類的 init() 方法前使用 required 關鍵字宣告訪問許可權。

例項

class classA { required init() { var a = 10 print(a) } } class classB: classA { required init() { var b = 30 print(b) } } let res = classA() let show = classB()

以上程式執行輸出結果為:

10
30
10

協議訪問許可權

如果想為一個協議明確的申明訪問級別,那麼需要注意一點,就是你要確保該協議只在你申明的訪問級別作用域中使用。

如果你定義了一個public訪問級別的協議,那麼實現該協議提供的必要函式也會是public的訪問級別。這一點不同於其他型別,比如,public訪問級別的其他型別,他們成員的訪問級別為internal。

例項

public protocol TcpProtocol { init(no1: Int) } public class MainClass { var no1: Int // local storage init(no1: Int) { self.no1 = no1 // initialization } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // Requires only one parameter for convenient method required override convenience init(no1: Int) { self.init(no1:no1, no2:0) } } let res = MainClass(no1: 20) let show = SubClass(no1: 30, no2: 50) print("res is: \(res.no1)") print("res is: \(show.no1)") print("res is: \(show.no2)")

以上程式執行輸出結果為:

res is: 20
res is: 30
res is: 50

擴充套件訪問許可權

你可以在條件允許的情況下對類、結構體、列舉進行擴充套件。擴充套件成員應該具有和原始類成員一致的訪問級別。比如你擴充套件了一個公共型別,那麼你新加的成員應該具有和原始成員一樣的預設的internal訪問級別。

或者,你可以明確申明擴充套件的訪問級別(比如使用private extension)給該擴充套件內所有成員申明一個新的預設訪問級別。這個新的預設訪問級別仍然可以被單獨成員所申明的訪問級別所覆蓋。


泛型訪問許可權

泛型型別或泛型函式的訪問級別取泛型型別、函式本身、泛型型別引數三者中的最低訪問級別。

例項

public struct TOS<T> { var items = [T]() private mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } var tos = TOS<String>() tos.push("Swift") print(tos.items) tos.push("泛型") print(tos.items) tos.push("型別引數") print(tos.items) tos.push("型別引數名") print(tos.items) let deletetos = tos.pop()

以上程式執行輸出結果為:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "型別引數"]
["Swift", "泛型", "型別引數", "型別引數名"]

類型別名

任何你定義的類型別名都會被當作不同的型別,以便於進行訪問控制。一個類型別名的訪問級別不可高於原型別的訪問級別。

比如說,一個private級別的類型別名可以設定給一個public、internal、private的型別,但是一個public級別的類型別名只能設定給一個public級別的型別,不能設定給internal或private 級別的型別。

注意:這條規則也適用於為滿足協議一致性而給相關型別命名別名的情況。

例項

public protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack<T>: Container { // original Stack<T> implementation var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // conformance to the Container protocol mutating func append(item: T) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> T { return items[i] } } func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool { // check that both containers contain the same number of items if someContainer.count != anotherContainer.count { return false } // check each pair of items to see if they are equivalent for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // all items match, so return true return true } var tos = Stack<String>() tos.push("Swift") print(tos.items) tos.push("泛型") print(tos.items) tos.push("Where 語句") print(tos.items) var eos = ["Swift", "泛型", "Where 語句"] print(eos)

以上程式執行輸出結果為:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 語句"]
["Swift", "泛型", "Where 語句"]