- 使用場(chǎng)景:
- 限制其他源文件和模塊對(duì)代碼的訪問(wèn)權(quán)限。
- 封裝隱藏代碼的實(shí)現(xiàn)細(xì)節(jié),只公開(kāi)接口給人調(diào)用
- 適用范圍:
- 給單個(gè)類(lèi)型(類(lèi)、結(jié)構(gòu)體、枚舉)設(shè)置訪問(wèn)級(jí)別
- 或單獨(dú)給這些類(lèi)型的屬性、方法、構(gòu)造器、下標(biāo)等設(shè)置訪問(wèn)級(jí)別
- 限定協(xié)議在一定訪問(wèn)級(jí)別的范圍內(nèi)使用
- 包括協(xié)議里的全局常量、變量和函數(shù)
- 給單個(gè)類(lèi)型(類(lèi)、結(jié)構(gòu)體、枚舉)設(shè)置訪問(wèn)級(jí)別
- 默認(rèn)的訪問(wèn)級(jí)別
- 不需代碼中都顯式聲明訪問(wèn)級(jí)別
- 開(kāi)發(fā)一個(gè)單 target 的應(yīng)用程序
- 完全可以不用顯式聲明代碼的訪問(wèn)級(jí)別
對(duì)代碼中可設(shè)置訪問(wèn)級(jí)別的特性(屬性、基本類(lèi)型、函數(shù)等),統(tǒng)一稱之為“實(shí)體”(entities)。
模塊和源文件 - Modules and Source Files
- 訪問(wèn)控制模型--兩個(gè)概念:
- 模塊:?jiǎn)我坏拇a分配單元——一個(gè)框架或應(yīng)用程序(a framework or application),一個(gè)模塊可使用
import關(guān)鍵字導(dǎo)入另外一個(gè)模塊- 源文件:一個(gè)模塊中的單個(gè) Swift 源代碼文件(事實(shí)上,是一個(gè)應(yīng)用程序或是框架中的單個(gè)文件),通常在單獨(dú)源文件中定義單個(gè)類(lèi)型,但是一個(gè)源文件可以包含多個(gè)類(lèi)型。函數(shù)等的定義
- 實(shí)體
- 源文件:一個(gè)模塊中的單個(gè) Swift 源代碼文件(事實(shí)上,是一個(gè)應(yīng)用程序或是框架中的單個(gè)文件),通常在單獨(dú)源文件中定義單個(gè)類(lèi)型,但是一個(gè)源文件可以包含多個(gè)類(lèi)型。函數(shù)等的定義
- 模塊:?jiǎn)我坏拇a分配單元——一個(gè)框架或應(yīng)用程序(a framework or application),一個(gè)模塊可使用
訪問(wèn)級(jí)別? - Access Levels
-
Swift 代碼實(shí)體的五個(gè)訪問(wèn)級(jí)別
-
Open(允許其他模塊,繼承和重寫(xiě)類(lèi)和類(lèi)成員) 和 public(禁止其他模塊,繼承和重寫(xiě)類(lèi):
- 范圍:可被本模塊中所有源文件可訪問(wèn),另一模塊的源文件訪問(wèn)需要導(dǎo)入本模塊
- 應(yīng)用:用 open 或 public 級(jí)別來(lái)指定框架的外部接口
-
Internal :
- 范圍:本模塊中所有源文件可訪問(wèn),其他模塊的源文件不能訪問(wèn)
- 應(yīng)用:接口只在應(yīng)用程序或框架內(nèi)部使用,設(shè)置為 internal 級(jí)別
-
File-private:
- 范圍:當(dāng)前定義源文件可訪問(wèn)
- 應(yīng)用:功能接口實(shí)現(xiàn),全在一個(gè)源文件,用 File-private 隱藏接口實(shí)現(xiàn)細(xì)節(jié)
-
private
- 范圍:在其定義的作用域可訪問(wèn) + 同一源文件內(nèi)的 extension 訪問(wèn)
- 應(yīng)用:接口只需在當(dāng)前作用域內(nèi)使用時(shí),用 private 來(lái)將其隱藏
-
Open(允許其他模塊,繼承和重寫(xiě)類(lèi)和類(lèi)成員) 和 public(禁止其他模塊,繼承和重寫(xiě)類(lèi):
訪問(wèn)級(jí)別基本原則
- 總體指導(dǎo)準(zhǔn)則 - overall guiding principle:實(shí)體不能定義在比自己訪問(wèn)級(jí)別低的實(shí)體中(至少要相同)
- 訪問(wèn)級(jí)別:實(shí)體 ≥ 定義實(shí)體的范圍
- 例子:
- 定義一個(gè) public 的變量的類(lèi)型,不能是 internal, file-private 或是 private,訪問(wèn)public 變量的地方,可能無(wú)法訪問(wèn)這個(gè)類(lèi)型的權(quán)限,從而無(wú)法訪問(wèn)該 public 變量
- 參數(shù)類(lèi)型、返回類(lèi)型 ≥ 函數(shù),否側(cè)可以調(diào)用函數(shù),但無(wú)法范圍參數(shù)和返回值
默認(rèn)訪問(wèn)級(jí)別
- 定義實(shí)體時(shí),不顯式指定訪問(wèn)級(jí)別,一般默認(rèn)訪問(wèn)級(jí)別為
internal(有一些情況會(huì)例外) - 數(shù)情況下,不需要明確指定實(shí)體的訪問(wèn)級(jí)別
單 target 應(yīng)用程序的訪問(wèn)級(jí)別
- 寫(xiě)單 target 應(yīng)用程序,代碼都在本應(yīng)用使用并且不會(huì)在應(yīng)用模塊之外使用,internal 已匹配這種需求
- 不需明確自定訪問(wèn)級(jí)別
- 若要對(duì)模塊中其他代碼隱藏接口實(shí)現(xiàn)細(xì)節(jié),標(biāo)注為 file private 或private
框架的訪問(wèn)級(jí)別 - Access Levels for Frameworks
- 因默認(rèn) internal,但框架接口要給外部調(diào)用,所以定義為 open 或 public
- 對(duì)外的接口,就是這個(gè)框架的 API
內(nèi)部實(shí)現(xiàn)仍可用默認(rèn)
internal,隱藏細(xì)節(jié)可用private或fileprivate框架的對(duì)外 API 部分,需要將它們?cè)O(shè)置為
open或public了
單元測(cè)試 target 的訪問(wèn)級(jí)別
- 默認(rèn) open 或 public 的才可跨模塊訪問(wèn)
- 應(yīng)用程序有單元測(cè)試 target 時(shí),測(cè)試模塊要訪問(wèn)應(yīng)用程序模塊的代碼
- 在導(dǎo)入應(yīng)用程序模塊的語(yǔ)句前使用
@testable特性 - 允許測(cè)試的編譯設(shè)置(
Build Options -> Enable Testability)下編譯這個(gè)應(yīng)用程序模塊 - 單元測(cè)試 target 就可以訪問(wèn)應(yīng)用程序模塊中所有內(nèi)部級(jí)別的實(shí)體
- 在導(dǎo)入應(yīng)用程序模塊的語(yǔ)句前使用
訪問(wèn)控制語(yǔ)法
- 通過(guò)修飾符
open、public、internal、fileprivate、private來(lái)聲明實(shí)體的訪問(wèn)級(jí)別:
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īng)標(biāo)注,否則都會(huì)使用默認(rèn)的 internal 訪問(wèn)級(jí)別
class SomeInternalClass {} // 隱式 internal
var someInternalConstant = 0 // 隱式 internal
自定義類(lèi)型
- 一個(gè)類(lèi)型的訪問(wèn)級(jí)別會(huì)影響類(lèi)型成員(屬性、方法、構(gòu)造器、下標(biāo))的默認(rèn)訪問(wèn)級(jí)別
- 將類(lèi)型定為
private或fileprivate,該類(lèi)型成員默認(rèn)訪問(wèn)級(jí)別也變成private或fileprivate級(jí)別 -
類(lèi)型指定為
internal或public(或者不明確指定訪問(wèn)級(jí)別,而使用默認(rèn)的internal),該類(lèi)型所有成員的默認(rèn)訪問(wèn)級(jí)別將是internal
一個(gè)
public類(lèi)型的所有成員的訪問(wèn)級(jí)別默認(rèn)為internal級(jí)別,而不是public級(jí)別如果你想將某個(gè)成員指定為
public級(jí)別,必須顯式指定這樣做的好處是,在你定義公共接口的時(shí)候,可以明確地選擇哪些接口是需要公開(kāi)的,哪些是內(nèi)部使用的,避免不小心將(類(lèi)型)內(nèi)部使用的接口公開(kāi)
public class SomePublicClass { // 顯式 public 類(lèi)
public var somePublicProperty = 0 // 顯式 public 類(lèi)成員
var someInternalProperty = 0 // 隱式 internal 類(lèi)成員
fileprivate func someFilePrivateMethod() {} // 顯式 fileprivate 類(lèi)成員
private func somePrivateMethod() {} // 顯式 private 類(lèi)成員
}
class SomeInternalClass { // 隱式 internal 類(lèi)
var someInternalProperty = 0 // 隱式 internal 類(lèi)成員
fileprivate func someFilePrivateMethod() {} // 顯式 fileprivate 類(lèi)成員
private func somePrivateMethod() {} // 顯式 private 類(lèi)成員
}
fileprivate class SomeFilePrivateClass { // 顯式 fileprivate 類(lèi)
func someFilePrivateMethod() {} // 隱式 fileprivate 類(lèi)成員
private func somePrivateMethod() {} // 顯式 private 類(lèi)成員
}
private class SomePrivateClass { // 顯式 private 類(lèi)
func somePrivateMethod() {} // 隱式 private 類(lèi)成員
}
元組類(lèi)型
- 由元級(jí)別最嚴(yán)格的類(lèi)型(元素)來(lái)決定
- 如,構(gòu)建一個(gè)包含兩種不同類(lèi)型的元組,其中一個(gè)為
internal,另一個(gè)類(lèi)型為private,那么這元組的訪問(wèn)級(jí)別為private。
元組不同于類(lèi)、結(jié)構(gòu)體、枚舉、函數(shù)那樣有單獨(dú)的定義。
一個(gè)元組的訪問(wèn)級(jí)別由元組中元素的訪問(wèn)級(jí)別來(lái)決定的,不能被顯式指定。
函數(shù)類(lèi)型
- 根據(jù)最嚴(yán)格的參數(shù)類(lèi)型或返回類(lèi)型的訪問(wèn)級(jí)別來(lái)決定
- 如不符合函數(shù)定義所在環(huán)境的默認(rèn)訪問(wèn)級(jí)別,需明確指定函數(shù)訪問(wèn)級(jí)別
- 按下面這種寫(xiě)法,代碼將無(wú)法通過(guò)編譯:
func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 此處是函數(shù)實(shí)現(xiàn)部分
}
- 返回類(lèi)型-該元組的訪問(wèn)級(jí)別是
private - 必須使明確用
private修飾符來(lái)明確指定該函數(shù)的訪問(wèn)級(jí)別
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 此處是函數(shù)實(shí)現(xiàn)部分
}
- 該函數(shù)當(dāng)做
public或internal級(jí)別來(lái)使用的話,可能會(huì)無(wú)法訪問(wèn)private級(jí)別的返回值
枚舉類(lèi)型
- 成員的訪問(wèn)級(jí)別和該枚舉類(lèi)型相同
- 不能為枚舉成員單獨(dú)指定不同的訪問(wèn)級(jí)別
public enum CompassPoint {
case north
case south
case east
case west
}
// CompassPoint 被明確指定為 public,那么它的成員 north、south、east、west 的訪問(wèn)級(jí)別同樣也是 public:
原始值和關(guān)聯(lián)值
- 原始值、關(guān)聯(lián)值的類(lèi)型的訪問(wèn)級(jí)別至少不能低于枚舉類(lèi)型的訪問(wèn)級(jí)別
- 如不能在一個(gè)
internal的枚舉中定義private的原始值類(lèi)型
嵌套類(lèi)型 - Nested Types
- 嵌套類(lèi)型的訪問(wèn)級(jí)別 = 包含它的類(lèi)型的訪問(wèn)級(jí)別
- private 級(jí)別的類(lèi)型中定義的嵌套類(lèi)型自動(dòng)為 private 級(jí)別
- fileprivate 級(jí)別的類(lèi)型中定義的嵌套類(lèi)型自動(dòng)為 fileprivate 級(jí)別
- public 或 internal 級(jí)別的類(lèi)型中定義的嵌套類(lèi)型自動(dòng)為 internal 級(jí)別
- 想讓嵌套類(lèi)型是 public 級(jí)別的,必須顯式指明為 public
子類(lèi)
- 可繼承同一模塊中的所有訪問(wèn)權(quán)限的類(lèi),也可繼承不同模塊被 open 修飾的類(lèi)
- 子類(lèi)不得高于父類(lèi)(子類(lèi) ≤ 父類(lèi))
- 如,父類(lèi)是
internal,子類(lèi)不能是public - 可重寫(xiě)類(lèi)成員(方法,屬性,初始化器或下標(biāo))
- 如,父類(lèi)是

- 提高父類(lèi)權(quán)限:對(duì) someMethod() 函數(shù)進(jìn)行了重寫(xiě)即改為“internal”級(jí)別,這比 someMethod() 的原本實(shí)現(xiàn)級(jí)別更高
public class A {
fileprivate func someMethod() {}
}
internal class B: A {
override internal func someMethod() {}
}
- 類(lèi) A 和子類(lèi) B 定義在同一個(gè)源文件中,那么 B 類(lèi)可以在 someMethod() 中調(diào)用父類(lèi)的 someMethod()
常量、變量、屬性、下標(biāo)
- 常量、變量、屬性不能擁有比它們類(lèi)型更高的訪問(wèn)級(jí)別。
- 如,你不能寫(xiě)一個(gè)public 的屬性而它的類(lèi)型是 private 的
- 下標(biāo)也不能擁有比索引類(lèi)型或返回類(lèi)型更高的訪問(wèn)級(jí)別
private var privateInstance = SomePrivateClass()
Getter 和 Setter
- getter 和 setter 和它們所屬常量、變量、屬性和下標(biāo)的訪問(wèn)級(jí)別相同
-
Setter的訪問(wèn)級(jí)別可低于Getter,從而控制讀寫(xiě)權(quán)限 - 語(yǔ)法:
var或subscript關(guān)鍵字之前,你可以通過(guò)fileprivate(set),private(set)或internal(set)為它們的寫(xiě)入權(quán)限指定更低的訪問(wèn)級(jí)別
這規(guī)則適用于存儲(chǔ)型和計(jì)算型屬性。
即使你不明確指定存儲(chǔ)型屬性的
Getter和Setter,Swift 也會(huì)隱式創(chuàng)建Getter和Setter
-
TrackedString的結(jié)構(gòu)體,記錄了value屬性被修改的次數(shù):
struct TrackedString {
private(set) var numberOfEdits = 0
var value: String = "" {
didSet {
numberOfEdits += 1
}
}
}
- numberOfEdits 屬性的 Getter 依然是默認(rèn)的訪問(wèn)級(jí)別 internal
-
Setter的訪問(wèn)級(jí)別是private,這表示該屬性只能在內(nèi)部修改,而在結(jié)構(gòu)體的外部則表現(xiàn)為一個(gè)只讀屬性
var stringToEdit = TrackedString()
stringToEdit.value = "This string will be tracked."
stringToEdit.value += " This edit will increment numberOfEdits."
stringToEdit.value += " So will this one."
print("The number of edits is \(stringToEdit.numberOfEdits)")
// 打印“The number of edits is 3”
- 可在其他的源文件中獲取
numberOfEdits屬性的值,但不能對(duì)其賦值
- 將
TrackedString結(jié)構(gòu)體明確為public - 結(jié)構(gòu)體的成員(包括
numberOfEdits屬性)擁有默認(rèn)的訪問(wèn)級(jí)別internal - 結(jié)合
public和private(set)修飾符- 把結(jié)構(gòu)體中的
numberOfEdits屬性的Getter的訪問(wèn)級(jí)別設(shè)置為public - 而
Setter的訪問(wèn)級(jí)別設(shè)置為private:
- 把結(jié)構(gòu)體中的
public struct TrackedString {
public private(set) var numberOfEdits = 0
public var value: String = "" {
didSet {
numberOfEdits += 1
}
}
public init() {}
}
構(gòu)造器
- 自定義構(gòu)造器
- 可低于或等于所屬類(lèi)型
- 必要構(gòu)造器
- 必須和所屬類(lèi)型的訪問(wèn)級(jí)別相同
- 類(lèi)似函數(shù)或方法,構(gòu)造器參數(shù)不能低于構(gòu)造器本身的訪問(wèn)級(jí)別
默認(rèn)構(gòu)造器
- Swift 會(huì)為結(jié)構(gòu)體和類(lèi)提供一個(gè)默認(rèn)的無(wú)參數(shù)的構(gòu)造器(前提條件:給存儲(chǔ)屬性賦初值 + 未定義構(gòu)造器)
- 默認(rèn)構(gòu)造器的訪問(wèn)級(jí)別與所屬類(lèi)型的訪問(wèn)級(jí)別相同
- 類(lèi)型被指定為
public級(jí)別,那么默認(rèn)構(gòu)造器的訪問(wèn)級(jí)別將為internal
- 類(lèi)型被指定為
- 希望在其他模塊中使用這種無(wú)參數(shù)的默認(rèn)構(gòu)造器,自己提供一個(gè)
public訪問(wèn)級(jí)別的無(wú)參數(shù)構(gòu)造器
結(jié)構(gòu)體默認(rèn)的成員逐一構(gòu)造器
- 任意存儲(chǔ)型屬性的訪問(wèn)級(jí)別為
private,成員逐一構(gòu)造器的訪問(wèn)級(jí)別就是private。否則,這種構(gòu)造器的訪問(wèn)級(jí)別依然是internal。 - 希望一個(gè)
public級(jí)別的結(jié)構(gòu)體也能在其他模塊中使用其默認(rèn)的成員逐一構(gòu)造器,只能自己提供一個(gè)public訪問(wèn)級(jí)別的成員逐一構(gòu)造器
協(xié)議
- 限制該協(xié)議只能在適當(dāng)?shù)脑L問(wèn)級(jí)別范圍內(nèi)被遵循。
- 協(xié)議中的每個(gè)方法或?qū)傩远急仨毢驮搮f(xié)議相同的訪問(wèn)級(jí)別
- 不能將協(xié)議中的方法或?qū)傩栽O(shè)置為其他訪問(wèn)級(jí)別
- 才能確保該協(xié)議的所有方法或?qū)傩詫?duì)于任意遵循者都可用。
協(xié)議繼承
- 新協(xié)議和被繼承協(xié)議的訪問(wèn)級(jí)別相同
- 如,不能將繼承自
internal協(xié)議的新協(xié)議定為public協(xié)議。
- 如,不能將繼承自
協(xié)議遵循
-
一個(gè)類(lèi)型可遵循比它級(jí)別更低的協(xié)議
- 一個(gè)
public級(jí)別類(lèi)型,如果遵循一個(gè)internal協(xié)議,遵循的部分只能在這internal協(xié)議所在的模塊中使用
- 一個(gè)
-
遵循了協(xié)議的類(lèi),取協(xié)議和類(lèi)的訪問(wèn)級(jí)別的最小者
- 如類(lèi)型是 public ,遵循協(xié)議 internal 級(jí)別,這個(gè)類(lèi)型就是 internal 級(jí)別的
-
寫(xiě)或擴(kuò)展一個(gè)類(lèi)型讓它遵循一個(gè)協(xié)議時(shí),類(lèi)按協(xié)議要求的實(shí)現(xiàn)方法與該協(xié)議的訪問(wèn)級(jí)別一致
- 一個(gè)
public類(lèi)型遵循一個(gè)internal協(xié)議,這個(gè)類(lèi)型對(duì)協(xié)議的所有實(shí)現(xiàn)至少都應(yīng)是internal級(jí)別的
- 一個(gè)
Swift 和 Objective-C 一樣,協(xié)議遵循是全局的,也就是說(shuō),在同一程序中,一個(gè)類(lèi)型不可能用兩種不同的方式實(shí)現(xiàn)同一個(gè)協(xié)議。
擴(kuò)展 - Extension
- Extension 的新增成員有和原始類(lèi)型成員一致的訪問(wèn)級(jí)別
- extension 一個(gè)
public或者internal類(lèi)型, extension 中的成員默認(rèn)為internal訪問(wèn)級(jí)別 - 用 extension 擴(kuò)展一個(gè)
fileprivate類(lèi)型,則 extension 中的成員默認(rèn)使用fileprivate訪問(wèn)級(jí)別 - 用 extension 擴(kuò)展了一個(gè)
private類(lèi)型,則 extension 的成員默認(rèn)使用private訪問(wèn)級(jí)別
- extension 一個(gè)
- 可以重新指定 extension 的默認(rèn)訪問(wèn)級(jí)別(例如,
private),從而給 extension 中所有成員一個(gè)新默認(rèn)訪問(wèn)級(jí)別 -
用 extension 來(lái)遵循協(xié)議的話,就不能顯式地聲明 extension 的訪問(wèn)級(jí)別
- extension 每個(gè) protocol 要求的實(shí)現(xiàn)都默認(rèn)使用 protocol 的訪問(wèn)級(jí)別
Extension 的私有成員
- 擴(kuò)展同一文件內(nèi)的類(lèi),結(jié)構(gòu)體或者枚舉,extension 里的代碼會(huì)表現(xiàn)得跟聲明在原類(lèi)型里的一模一樣。也就是說(shuō)你可以這樣:
- 在類(lèi)型的聲明里,聲明一個(gè)私有成員,在同一文件的 extension 里訪問(wèn)。
- 在 extension 里聲明一個(gè)私有成員,在同一文件的另一個(gè) extension 里訪問(wèn)。
- 在 extension 里聲明一個(gè)私有成員,在同一文件的類(lèi)型聲明里訪問(wèn)。
- 可以使用 extension 來(lái)組織你的代碼,而且不受私有成員的影響
protocol SomeProtocol {
func doSomething()
}
- 使用 extension 來(lái)遵循協(xié)議,就像這樣:
struct SomeStruct {
private var privateVariable = 12
}
extension SomeStruct: SomeProtocol {
func doSomething() {
print(privateVariable)
}
}
泛型
- 泛型類(lèi)型或泛型函數(shù),取決于泛型類(lèi)型或泛型函數(shù)本身的訪問(wèn)級(jí)別
- 還需結(jié)合類(lèi)型參數(shù)的類(lèi)型約束的訪問(wèn)級(jí)別
- 根據(jù)這些訪問(wèn)級(jí)別中的最低訪問(wèn)級(jí)別來(lái)確定
類(lèi)型別名
- 類(lèi)型別名的訪問(wèn)級(jí)別,不能高于原類(lèi)型
-
private級(jí)別的類(lèi)型別名可以作為private、fileprivate、internal、public或者open類(lèi)型的別名 - 但是
public級(jí)別的類(lèi)型別名只能作為public類(lèi)型的別名,不能作為internal、fileprivate或private類(lèi)型的別名。
-
這條規(guī)則也適用于為滿足協(xié)議遵循而將類(lèi)型別名用于關(guān)聯(lián)類(lèi)型的情況。
