訪問控制
訪問控制:用來控制代碼是否需要暴露給調(diào)用方的一種設(shè)置,比如可以隱藏一些代碼的實現(xiàn)細(xì)節(jié)等等
- 可以為類、結(jié)構(gòu)體、枚舉設(shè)置訪問細(xì)節(jié)
- 可以為類(結(jié)構(gòu)體、枚舉)中屬性、方法、構(gòu)造器等設(shè)置訪問級別
- Swift 為某些典型場景提供了默認(rèn)的訪問級別,這樣就不需要我們在每段代碼中都申明顯式訪問級別
注意
為了簡單起見,對于代碼中可以設(shè)置訪問級別的特性(屬性、基本類型、函數(shù)等),在下面的內(nèi)容中我們會稱之為“實體”
模塊和源文件
Swift 中的訪問控制模型基于模塊和源文件這兩個概念。
- 模塊:一個模塊是代碼分布中一個單一的單元。比如一個能被其它模塊通過 import 關(guān)鍵字導(dǎo)入的framework 或 程序
- 源文件:開發(fā)自己在工程里新建的代碼文件
訪問級別
- Swift 為代碼中的實體提供了五種不同的訪問級別
| 內(nèi)容 | 說明 |
|---|---|
| Open | 只能作用于類和類的成員,可以被任何人使用,包括重寫和繼承 |
| Public | 可以被任何人使用。但其他模塊中不可以被重寫和繼承,而在本模塊內(nèi)可以被重寫和繼承 |
| Internal(默認(rèn)訪問級別,修飾符可寫可不寫) | 所修飾的屬性或方法在源代碼所在的整個模塊都可以訪問。如果是框架或者庫代碼,則在整個框架內(nèi)部都可以訪問,框架由外部代碼所引用時,則不可以訪問。如果是App代碼,也是在整個App代碼,也是在整個App內(nèi)部可以訪問 |
| File-private | 所修飾的屬性或者方法在當(dāng)前的源文件里可以訪問 |
| Private | 訪問級別所修飾的屬性或者方法只能在當(dāng)前類里訪問 |
Open 為最高訪問級別(限制最少),Private 為最低訪問級別(限制最多)。
單個 target 應(yīng)用程序的訪問級別
當(dāng)你編寫一個單個的Target APP時,應(yīng)用的所有功能都是為該應(yīng)用服務(wù),而不需要提供給其他應(yīng)用或者模塊使用,所以我們不需要明確設(shè)置訪問級別,使用默認(rèn)的訪問級別 Internal 即可。但是,你也可以使用 fileprivate 訪問或 private 訪問級別,用于隱藏一些功能的實現(xiàn)細(xì)節(jié)框架的訪問級別
當(dāng)你開發(fā)框架時,就需要把一些對外的接口定義為 Open 或 Public,以便使用者導(dǎo)入該框架后可以正常使用其功能。這些被你定義為對外的接口,就是這個框架的 API
框架依然會使用默認(rèn)的 internal ,也可以指定為 fileprivate 訪問或者 private 訪問級別。當(dāng)你想把某個實體作為框架的 API 的時候,需顯式為其指定開放訪問或公開訪問級別單元測試 target 的訪問級別
當(dāng)你的應(yīng)用程序包含單元測試 target 時,為了測試,測試模塊需要訪問應(yīng)用程序模塊中的代碼。默認(rèn)情況下只有 open 或 public 級別的實體才可以被其他模塊訪問。然而,如果在導(dǎo)入應(yīng)用程序模塊的語句前使用 @testable 特性,然后在允許測試的編譯設(shè)置(Build Options -> Enable Testability)下編譯這個應(yīng)用程序模塊,單元測試目標(biāo)就可以訪問應(yīng)用程序模塊中所有內(nèi)部級別的實體
訪問控制語法
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() {}
自定義類型
- 如果想為一個自定義的類指定訪問級別,在定義類時進(jìn)行指定即可。新建的類只能在它的訪問級別限制范圍內(nèi)使用
fileprivate class SomeFilePrivateClass {
func someFilePrivateMethod() {}
func somePrivateMethod() {}
}
一個類的訪問級別也會影響到類型成員(屬性、方法、構(gòu)造器、下標(biāo))的默認(rèn)訪問級別。如果你將類指定為 private 或者 fileprivate 級別,那么該類的所有成員的默認(rèn)訪問級別也會變成 private 或者 fileprivate 級別,如果你將類指定為公開或者 internal (或者不明確指定訪問級別,而使用默認(rèn)的 internal ),那么該類型的所有成員的默認(rèn)訪問級別將是內(nèi)部訪問
一個 public 類型的所有成員的訪問級別默認(rèn)為 internal 級別,而不是 public 級別。如果你想將某個成員指定為 public 級別,那么你必須顯式指定。這樣做的好處是,在你定義公共接口的時候,可以明確地選擇哪些接口是需要公開的,哪些是內(nèi)部使用的,避免不小心將內(nèi)部使用的接口公開
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 類成員
}
- 元組類型
元組的訪問級別將由元組中訪問級別最嚴(yán)格的類型來決定,例如,如果你構(gòu)建了一個包含兩種不同類型的元組,其中一個類型為 internal,另一個類型為 private,那么這個元組的訪問級別為 private - 函數(shù)類型
函數(shù)的訪問級別根據(jù)訪問級別最嚴(yán)格的參數(shù)類型或返回類型的訪問級別來決定 - 枚舉類型
枚舉成員的訪問級別和該枚舉類型相同,你不能為枚舉成員單獨指定不同的訪問級別
子類
- 子類的訪問級別不得高于父類的訪問級別,例如,父類的訪問級別是 internal,子類的訪問級別就不能是 public
- 可以通過重寫為繼承來的類成員提供更高的訪問級別
public class ClassA{
fileprivate func run(){
}
}
internal class ClassB:ClassA{
override internal func run(){
super.run()
}
}
常量、變量、屬性、下標(biāo)
常量、變量、屬性不能擁有比它們的類型更高的訪問級別,比如定義一個public 級別的屬性,那么它的訪問級別不能是Internal
構(gòu)造器
-自定義構(gòu)造器的訪問級別可以低于或等于其所屬類的訪問級別。唯一的例外是必要構(gòu)造器,它的訪問級別必須和所屬類型的訪問級別相同
- 默認(rèn)構(gòu)造器的訪問級別與所屬類的訪問級別相同,除非類的訪問級別是 public。如果一個類型被指定為 public 級別,那么默認(rèn)構(gòu)造器的訪問級別將為 internal。如果你希望一個 public 級別的類型也能在其他模塊中使用這種無參數(shù)的默認(rèn)構(gòu)造器,你只能自己提供一個 public 訪問級別的無參數(shù)構(gòu)造器
協(xié)議
- 如果想為一個協(xié)議明確地指定訪問級別,在定義協(xié)議時指定即可。那么協(xié)議只能在限制的訪問級別內(nèi)使用
- 協(xié)議繼承中,如果定義了一個繼承自其他協(xié)議的新協(xié)議,那么新協(xié)議擁有的訪問級別最高也只能和被繼承協(xié)議的訪問級別相同。例如,你不能將繼承自 internal 協(xié)議的新協(xié)議定義為 public 協(xié)議
- 一個類型可以采納比自身訪問級別低的協(xié)議。例如,你可以定義一個 public 級別的類,它可以在其他模塊中使用,同時它也可以采納一個 internal 級別的協(xié)議,但是只能在該協(xié)議所在的模塊中作為符合該協(xié)議的類使用
分類
- 使用 extension 擴展了一個 public 或者 internal 類型,extension 中的成員就默認(rèn)使用 internal 訪問級別,和原始類型中的成員一致。如果你使用 extension 擴展了一個 private 類型,則 extension 的成員默認(rèn)使用 private 訪問級別
- 使用 extension 來遵循協(xié)議的話,就不能顯式地聲明 extension 的訪問級別。extension 每個 protocol 要求的實現(xiàn)都默認(rèn)使用 protocol 的訪問級別
泛型
泛型類型或泛型函數(shù)的訪問級別取決于泛型類型或泛型函數(shù)本身的訪問級別,還需結(jié)合類型參數(shù)的類型約束的訪問級別,根據(jù)這些訪問級別中的最低訪問級別來確定