訪問控制可以限定其他源文件或模塊中的代碼對(duì)你的代碼的訪問級(jí)別。這個(gè)特性可以讓我們隱藏代碼的一些實(shí)現(xiàn)細(xì)節(jié)朽色,并且可以為其他人可以訪問和使用的代碼提供接口邻吞。
你可以明確地給單個(gè)類型(類、結(jié)構(gòu)體葫男、枚舉)設(shè)置訪問級(jí)別抱冷,也可以給這些類型的屬性、方法梢褐、構(gòu)造器旺遮、下標(biāo)等設(shè)置訪問級(jí)別。協(xié)議也可以被限定在一定的范圍內(nèi)使用盈咳,包括協(xié)議里的全局常量耿眉、變量和函數(shù)。
Swift 不僅提供了多種不同的訪問級(jí)別鱼响,還為某些典型場景提供了默認(rèn)的訪問級(jí)別跷敬,這樣就不需要我們在每段代碼中都申明顯式訪問級(jí)別。其實(shí)热押,如果只是開發(fā)一個(gè)單一 target 的應(yīng)用程序,我們完全可以不用顯式申明代碼的訪問級(jí)別斤寇。
訪問級(jí)別
Swift 為代碼中的實(shí)體提供了三種不同的訪問級(jí)別桶癣。這些訪問級(jí)別不僅與源文件中定義的實(shí)體相關(guān),同時(shí)也與源文件所屬的模塊相關(guān)娘锁。
-
public
:可以訪問同一模塊源文件中的任何實(shí)體牙寞,在模塊外也可以通過導(dǎo)入該模塊來訪問源文件里的所有實(shí)體。通常情況下,框架中的某個(gè)接口可以被任何人使用時(shí)间雀,你可以將其設(shè)置為public
級(jí)別悔详。 -
internal
:可以訪問同一模塊源文件中的任何實(shí)體,但是不能從模塊外訪問該模塊源文件中的實(shí)體惹挟。通常情況下茄螃,某個(gè)接口只在應(yīng)用程序或框架內(nèi)部使用時(shí),你可以將其設(shè)置為internal
級(jí)別连锯。 -
private
:限制實(shí)體只能在所在的源文件內(nèi)部使用归苍。使用private
級(jí)別可以隱藏某些功能的實(shí)現(xiàn)細(xì)節(jié)。
public
為最高(限制最少)訪問級(jí)別运怖,private
為最低(限制最多)訪問級(jí)別拼弃。
ps: Swift 中的
private
訪問級(jí)別不同于其他語言,它的范圍限于源文件摇展,而不是聲明范圍內(nèi)吻氧。這就意味著,一個(gè)類型可以訪問其所在源文件中的所有private
實(shí)體咏连,但是如果它的擴(kuò)展定義在其他源文件中盯孙,那么它的擴(kuò)展就不能訪問它在這個(gè)源文件中定義的private
實(shí)體。
訪問級(jí)別基本原則
Swift 中的訪問級(jí)別遵循一個(gè)基本原則:不可以在某個(gè)實(shí)體中定義訪問級(jí)別更高的實(shí)體捻勉。
例如:
- 一個(gè)
public
訪問級(jí)別的變量镀梭,其類型的訪問級(jí)別不能是internal
或private
。因?yàn)闊o法保證變量的類型在使用變量的地方也具有訪問權(quán)限踱启。 - 函數(shù)的訪問級(jí)別不能高于它的參數(shù)類型和返回類型的訪問級(jí)別报账。因?yàn)槿绻瘮?shù)定義為
public
而參數(shù)類型或者返回類型定義為internal
或private
,就會(huì)出現(xiàn)函數(shù)可以在任何地方被訪問埠偿,但是它的參數(shù)類型和返回類型卻不可以透罢。
默認(rèn)訪問級(jí)別
如果你不為代碼中的實(shí)體顯式指定訪問級(jí)別,那么它們默認(rèn)為 internal
級(jí)別(有一些例外情況冠蒋,稍后會(huì)進(jìn)行說明)羽圃。因此,在大多數(shù)情況下抖剿,我們不需要顯式指定實(shí)體的訪問級(jí)別朽寞。
框架的訪問級(jí)別
當(dāng)你開發(fā)框架時(shí),就需要把一些對(duì)外的接口定義為 public
級(jí)別斩郎,以便使用者導(dǎo)入該框架后可以正常使用其功能脑融。這些被你定義為 public
的接口,就是這個(gè)框架的 API缩宜。
ps: 框架依然會(huì)使用默認(rèn)的
internal
級(jí)別肘迎,也可以指定為private
級(jí)別甥温。當(dāng)你想把某個(gè)實(shí)體作為框架的 API 的時(shí)候,需顯式為其指定public
級(jí)別妓布。
單元測試 target 的訪問級(jí)別
當(dāng)你的應(yīng)用程序包含單元測試 target 時(shí)姻蚓,為了測試,測試模塊需要訪問應(yīng)用程序模塊中的代碼匣沼。默認(rèn)情況下只有 public
級(jí)別的實(shí)體才可以被其他模塊訪問狰挡。然而,如果在導(dǎo)入應(yīng)用程序模塊的語句前使用 @testable
特性肛著,然后在允許測試的編譯設(shè)置(Build Options -> Enable Testability
)下編譯這個(gè)應(yīng)用程序模塊圆兵,單元測試 target 就可以訪問應(yīng)用程序模塊中所有 internal
級(jí)別的實(shí)體。
語法
public class SomePublicClass {}
internal class SomeInternalClass {}
private class SomePrivateClass {}
public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}
自定義類型
一個(gè)類型的訪問級(jí)別也會(huì)影響到類型成員(屬性枢贿、方法殉农、構(gòu)造器、下標(biāo))的默認(rèn)訪問級(jí)別局荚。如果你將類型指定為 private
級(jí)別超凳,那么該類型的所有成員的默認(rèn)訪問級(jí)別也會(huì)變成 private
。如果你將類型指定為 public
或者 internal
級(jí)別(或者不明確指定訪問級(jí)別耀态,而使用默認(rèn)的 internal
訪問級(jí)別)轮傍,那么該類型的所有成員的默認(rèn)訪問級(jí)別將是 internal
。
ps: 上面提到首装,一個(gè)
public
類型的所有成員的訪問級(jí)別默認(rèn)為internal
級(jí)別创夜,而不是public
級(jí)別。如果你想將某個(gè)成員指定為public
級(jí)別仙逻,那么你必須顯式指定驰吓。這樣做的好處是,在你定義公共接口的時(shí)候系奉,可以明確地選擇哪些接口是需要公開的檬贰,哪些是內(nèi)部使用的,避免不小心將內(nèi)部使用的接口公開缺亮。
public class SomePublicClass { // 顯式的 public 類
public var somePublicProperty = 0 // 顯式的 public 類成員
var someInternalProperty = 0 // 隱式的 internal 類成員
private func somePrivateMethod() {} // 顯式的 private 類成員
}
class SomeInternalClass { // 隱式的 internal 類
var someInternalProperty = 0 // 隱式的 internal 類成員
private func somePrivateMethod() {} // 顯式的 private 類成員
}
private class SomePrivateClass { // 顯式的 private 類
var somePrivateProperty = 0 // 隱式的 private 類成員
func somePrivateMethod() {} // 隱式的 private 類成員
}
元組類型
元組的訪問級(jí)別將由元組中訪問級(jí)別最嚴(yán)格的類型來決定翁涤。例如,如果你構(gòu)建了一個(gè)包含兩種不同類型的元組萌踱,其中一個(gè)類型為 internal
級(jí)別葵礼,另一個(gè)類型為 private
級(jí)別,那么這個(gè)元組的訪問級(jí)別為 private
并鸵。
ps: 元組不同于類章咧、結(jié)構(gòu)體、枚舉能真、函數(shù)那樣有單獨(dú)的定義。元組的訪問級(jí)別是在它被使用時(shí)自動(dòng)推斷出的,而無法明確指定粉铐。
函數(shù)類型
函數(shù)的訪問級(jí)別根據(jù)訪問級(jí)別最嚴(yán)格的參數(shù)類型或返回類型的訪問級(jí)別來決定疼约。但是,如果這種訪問級(jí)別不符合函數(shù)定義所在環(huán)境的默認(rèn)訪問級(jí)別蝙泼,那么就需要明確地指定該函數(shù)的訪問級(jí)別程剥。
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 此處是函數(shù)實(shí)現(xiàn)部分
}
將該函數(shù)指定為 public
或 internal
,或者使用默認(rèn)的訪問級(jí)別 internal
都是錯(cuò)誤的汤踏,因?yàn)槿绻言摵瘮?shù)當(dāng)做 public
或 internal
級(jí)別來使用的話织鲸,可能會(huì)無法訪問 private
級(jí)別的返回值。
枚舉類型
枚舉成員的訪問級(jí)別和該枚舉類型相同溪胶,你不能為枚舉成員單獨(dú)指定不同的訪問級(jí)別搂擦。
枚舉定義中的任何原始值或關(guān)聯(lián)值的類型的訪問級(jí)別至少不能低于枚舉類型的訪問級(jí)別。例如哗脖,你不能在一個(gè) internal
訪問級(jí)別的枚舉中定義 private 級(jí)別的原始值類型瀑踢。
嵌套類型
如果在 private
級(jí)別的類型中定義嵌套類型,那么該嵌套類型就自動(dòng)擁有 private
訪問級(jí)別才避。如果在 public
或者 internal
級(jí)別的類型中定義嵌套類型橱夭,那么該嵌套類型自動(dòng)擁有 internal
訪問級(jí)別。如果想讓嵌套類型擁有 public
訪問級(jí)別桑逝,那么需要明確指定該嵌套類型的訪問級(jí)別棘劣。
子類
子類的訪問級(jí)別不得高于父類的訪問級(jí)別。例如楞遏,父類的訪問級(jí)別是 internal
茬暇,子類的訪問級(jí)別就不能是 public
。
可以通過重寫為繼承來的類成員提供更高的訪問級(jí)別橱健。
public class A {
private func someMethod() {}
}
internal class B: A {
override internal func someMethod() {
super.someMethod()
}
}
常量而钞、變量、屬性拘荡、下標(biāo)
常量臼节、變量、屬性不能擁有比它們的類型更高的訪問級(jí)別珊皿。例如网缝,你不能定義一個(gè) public
級(jí)別的屬性,但是它的類型卻是 private
級(jí)別的蟋定。同樣粉臊,下標(biāo)也不能擁有比索引類型或返回類型更高的訪問級(jí)別。
Getter 和 Setter
常量驶兜、變量扼仲、屬性远寸、下標(biāo)的 Getters
和 Setters
的訪問級(jí)別和它們所屬類型的訪問級(jí)別相同。
Setter
的訪問級(jí)別可以低于對(duì)應(yīng)的 Getter
的訪問級(jí)別屠凶,這樣就可以控制變量驰后、屬性或下標(biāo)的讀寫權(quán)限。在 var
或 subscript
關(guān)鍵字之前矗愧,你可以通過 private(set)
或 internal(set)
為它們的寫入權(quán)限指定更低的訪問級(jí)別灶芝。
ps: 這個(gè)規(guī)則同時(shí)適用于存儲(chǔ)型屬性和計(jì)算型屬性。即使你不明確指定存儲(chǔ)型屬性的
Getter
和Setter
唉韭,Swift 也會(huì)隱式地為其創(chuàng)建Getter
和Setter
夜涕,用于訪問該屬性的后備存儲(chǔ)。使用private(set)
和internal(set)
可以改變Setter
的訪問級(jí)別属愤,這對(duì)計(jì)算型屬性也同樣適用女器。
struct TrackedString {
private(set) var numberOfEdits = 0
var value: String = "" {
didSet {
numberOfEdits++
}
}
}
構(gòu)造器
自定義構(gòu)造器的訪問級(jí)別可以低于或等于其所屬類型的訪問級(jí)別。唯一的例外是必要構(gòu)造器春塌,它的訪問級(jí)別必須和所屬類型的訪問級(jí)別相同晓避。
如同函數(shù)或方法的參數(shù),構(gòu)造器參數(shù)的訪問級(jí)別也不能低于構(gòu)造器本身的訪問級(jí)別只壳。
默認(rèn)構(gòu)造器
默認(rèn)構(gòu)造器的訪問級(jí)別與所屬類型的訪問級(jí)別相同俏拱,除非類型的訪問級(jí)別是 public
。如果一個(gè)類型被指定為 public
級(jí)別吼句,那么默認(rèn)構(gòu)造器的訪問級(jí)別將為 internal
锅必。如果你希望一個(gè) public
級(jí)別的類型也能在其他模塊中使用這種無參數(shù)的默認(rèn)構(gòu)造器,你只能自己提供一個(gè) public
訪問級(jí)別的無參數(shù)構(gòu)造器惕艳。
結(jié)構(gòu)體默認(rèn)的成員逐一構(gòu)造器
如果結(jié)構(gòu)體中任意存儲(chǔ)型屬性的訪問級(jí)別為 private
搞隐,那么該結(jié)構(gòu)體默認(rèn)的成員逐一構(gòu)造器的訪問級(jí)別就是 private
。否則远搪,這種構(gòu)造器的訪問級(jí)別依然是 internal
劣纲。
協(xié)議
如果想為一個(gè)協(xié)議類型明確地指定訪問級(jí)別,在定義協(xié)議時(shí)指定即可谁鳍。這將限制該協(xié)議只能在適當(dāng)?shù)脑L問級(jí)別范圍內(nèi)被采納癞季。
協(xié)議中的每一個(gè)要求都具有和該協(xié)議相同的訪問級(jí)別。你不能將協(xié)議中的要求設(shè)置為其他訪問級(jí)別倘潜。這樣才能確保該協(xié)議的所有要求對(duì)于任意采納者都將可用绷柒。
ps: 如果你定義了一個(gè)
public
訪問級(jí)別的協(xié)議,那么該協(xié)議的所有實(shí)現(xiàn)也會(huì)是public
訪問級(jí)別涮因。這一點(diǎn)不同于其他類型废睦,例如,當(dāng)類型是public
訪問級(jí)別時(shí)养泡,其成員的訪問級(jí)別卻只是internal
嗜湃。
協(xié)議繼承
如果定義了一個(gè)繼承自其他協(xié)議的新協(xié)議奈应,那么新協(xié)議擁有的訪問級(jí)別最高也只能和被繼承協(xié)議的訪問級(jí)別相同。例如购披,你不能將繼承自 internal
協(xié)議的新協(xié)議定義為 public
協(xié)議钥组。
協(xié)議一致性
一個(gè)類型可以采納比自身訪問級(jí)別低的協(xié)議。例如今瀑,你可以定義一個(gè) public
級(jí)別的類型,它可以在其他模塊中使用点把,同時(shí)它也可以采納一個(gè) internal
級(jí)別的協(xié)議橘荠,但是只能在該協(xié)議所在的模塊中作為符合該協(xié)議的類型使用。
采納了協(xié)議的類型的訪問級(jí)別取它本身和所采納協(xié)議兩者間最低的訪問級(jí)別郎逃。也就是說如果一個(gè)類型是 public
級(jí)別哥童,采納的協(xié)議是 internal
級(jí)別,那么采納了這個(gè)協(xié)議后褒翰,該類型作為符合協(xié)議的類型時(shí)贮懈,其訪問級(jí)別也是 internal
。
如果你采納了協(xié)議优训,那么實(shí)現(xiàn)了協(xié)議的所有要求后朵你,你必須確保這些實(shí)現(xiàn)的訪問級(jí)別不能低于協(xié)議的訪問級(jí)別。例如揣非,一個(gè) public
級(jí)別的類型抡医,采納了 internal
級(jí)別的協(xié)議,那么協(xié)議的實(shí)現(xiàn)至少也得是 internal
級(jí)別早敬。
ps: Swift 和 Objective-C 一樣忌傻,協(xié)議的一致性是全局的,也就是說搞监,在同一程序中水孩,一個(gè)類型不可能用兩種不同的方式實(shí)現(xiàn)同一個(gè)協(xié)議。
擴(kuò)展
你可以在訪問級(jí)別允許的情況下對(duì)類琐驴、結(jié)構(gòu)體俘种、枚舉進(jìn)行擴(kuò)展。擴(kuò)展成員具有和原始類型成員一致的訪問級(jí)別棍矛。例如安疗,你擴(kuò)展了一個(gè) public
或者 internal
類型,擴(kuò)展中的成員具有默認(rèn)的 internal
訪問級(jí)別够委,和原始類型中的成員一致 荐类。如果你擴(kuò)展了一個(gè) private
類型,擴(kuò)展成員則擁有默認(rèn)的 private
訪問級(jí)別茁帽。
或者玉罐,你可以明確指定擴(kuò)展的訪問級(jí)別(例如屈嗤,private extension
),從而給該擴(kuò)展中的所有成員指定一個(gè)新的默認(rèn)訪問級(jí)別吊输。這個(gè)新的默認(rèn)訪問級(jí)別仍然可以被單獨(dú)指定的訪問級(jí)別所覆蓋饶号。
通過擴(kuò)展添加協(xié)議一致性
如果你通過擴(kuò)展來采納協(xié)議,那么你就不能顯式指定該擴(kuò)展的訪問級(jí)別了季蚂。協(xié)議擁有相應(yīng)的訪問級(jí)別茫船,并會(huì)為該擴(kuò)展中所有協(xié)議要求的實(shí)現(xiàn)提供默認(rèn)的訪問級(jí)別。
泛型
泛型類型或泛型函數(shù)的訪問級(jí)別取決于泛型類型或泛型函數(shù)本身的訪問級(jí)別扭屁,還需結(jié)合類型參數(shù)的類型約束的訪問級(jí)別算谈,根據(jù)這些訪問級(jí)別中的最低訪問級(jí)別來確定。
類型別名
你定義的任何類型別名都會(huì)被當(dāng)作不同的類型料滥,以便于進(jìn)行訪問控制然眼。類型別名的訪問級(jí)別不可高于其表示的類型的訪問級(jí)別。例如葵腹,private
級(jí)別的類型別名可以作為 public
高每、internal
、private
類型的別名践宴,但是 public
級(jí)別的類型別名只能作為 public
類型的別名鲸匿,不能作為 internal
或 private
類型的別名。
ps: 這條規(guī)則也適用于為滿足協(xié)議一致性而將類型別名用于關(guān)聯(lián)類型的情況浴井。