- 使用場景:
- 限制其他源文件和模塊對代碼的訪問權(quán)限搔体。
- 封裝隱藏代碼的實現(xiàn)細(xì)節(jié)恨樟,只公開接口給人調(diào)用
- 適用范圍:
- 給單個類型(類、結(jié)構(gòu)體疚俱、枚舉)設(shè)置訪問級別
- 或單獨給這些類型的屬性劝术、方法、構(gòu)造器、下標(biāo)等設(shè)置訪問級別
- 限定協(xié)議在一定訪問級別的范圍內(nèi)使用
- 包括協(xié)議里的全局常量养晋、變量和函數(shù)
- 給單個類型(類、結(jié)構(gòu)體疚俱、枚舉)設(shè)置訪問級別
- 默認(rèn)的訪問級別
- 不需代碼中都顯式聲明訪問級別
- 開發(fā)一個單 target 的應(yīng)用程序
- 完全可以不用顯式聲明代碼的訪問級別
對代碼中可設(shè)置訪問級別的特性(屬性衬吆、基本類型、函數(shù)等)匙握,統(tǒng)一稱之為“實體”(entities)咆槽。
模塊和源文件 - Modules and Source Files
- 訪問控制模型--兩個概念:
- 模塊:單一的代碼分配單元——一個框架或應(yīng)用程序(a framework or application),一個模塊可使用
import
關(guān)鍵字導(dǎo)入另外一個模塊- 源文件:一個模塊中的單個 Swift 源代碼文件(事實上圈纺,是一個應(yīng)用程序或是框架中的單個文件)秦忿,通常在單獨源文件中定義單個類型,但是一個源文件可以包含多個類型蛾娶。函數(shù)等的定義
- 實體
- 源文件:一個模塊中的單個 Swift 源代碼文件(事實上圈纺,是一個應(yīng)用程序或是框架中的單個文件)秦忿,通常在單獨源文件中定義單個類型,但是一個源文件可以包含多個類型蛾娶。函數(shù)等的定義
- 模塊:單一的代碼分配單元——一個框架或應(yīng)用程序(a framework or application),一個模塊可使用
訪問級別? - Access Levels
-
Swift 代碼實體的五個訪問級別
-
Open(允許其他模塊灯谣,繼承和重寫類和類成員) 和 public(禁止其他模塊,繼承和重寫類:
- 范圍:可被本模塊中所有源文件可訪問蛔琅,另一模塊的源文件訪問需要導(dǎo)入本模塊
- 應(yīng)用:用 open 或 public 級別來指定框架的外部接口
-
Internal :
- 范圍:本模塊中所有源文件可訪問胎许,其他模塊的源文件不能訪問
- 應(yīng)用:接口只在應(yīng)用程序或框架內(nèi)部使用,設(shè)置為 internal 級別
-
File-private:
- 范圍:當(dāng)前定義源文件可訪問
- 應(yīng)用:功能接口實現(xiàn)罗售,全在一個源文件辜窑,用 File-private 隱藏接口實現(xiàn)細(xì)節(jié)
-
private
- 范圍:在其定義的作用域可訪問 + 同一源文件內(nèi)的 extension 訪問
- 應(yīng)用:接口只需在當(dāng)前作用域內(nèi)使用時,用 private 來將其隱藏
-
Open(允許其他模塊灯谣,繼承和重寫類和類成員) 和 public(禁止其他模塊,繼承和重寫類:
訪問級別基本原則
- 總體指導(dǎo)準(zhǔn)則 - overall guiding principle:實體不能定義在比自己訪問級別低的實體中(至少要相同)
- 訪問級別:實體 ≥ 定義實體的范圍
- 例子:
- 定義一個 public 的變量的類型寨躁,不能是 internal, file-private 或是 private穆碎,訪問public 變量的地方,可能無法訪問這個類型的權(quán)限职恳,從而無法訪問該 public 變量
- 參數(shù)類型所禀、返回類型 ≥ 函數(shù),否側(cè)可以調(diào)用函數(shù)放钦,但無法范圍參數(shù)和返回值
默認(rèn)訪問級別
- 定義實體時色徘,不顯式指定訪問級別,一般默認(rèn)訪問級別為
internal
(有一些情況會例外) - 數(shù)情況下操禀,不需要明確指定實體的訪問級別
單 target 應(yīng)用程序的訪問級別
- 寫單 target 應(yīng)用程序褂策,代碼都在本應(yīng)用使用并且不會在應(yīng)用模塊之外使用,internal 已匹配這種需求
- 不需明確自定訪問級別
- 若要對模塊中其他代碼隱藏接口實現(xiàn)細(xì)節(jié)颓屑,標(biāo)注為 file private 或private
框架的訪問級別 - Access Levels for Frameworks
- 因默認(rèn) internal辙培,但框架接口要給外部調(diào)用,所以定義為 open 或 public
- 對外的接口邢锯,就是這個框架的 API
內(nèi)部實現(xiàn)仍可用默認(rèn)
internal
,隱藏細(xì)節(jié)可用private
或fileprivate
框架的對外 API 部分搀别,需要將它們設(shè)置為
open
或public
了
單元測試 target 的訪問級別
- 默認(rèn) open 或 public 的才可跨模塊訪問
- 應(yīng)用程序有單元測試 target 時丹擎,測試模塊要訪問應(yīng)用程序模塊的代碼
- 在導(dǎo)入應(yīng)用程序模塊的語句前使用
@testable
特性 - 允許測試的編譯設(shè)置(
Build Options -> Enable Testability
)下編譯這個應(yīng)用程序模塊 - 單元測試 target 就可以訪問應(yīng)用程序模塊中所有內(nèi)部級別的實體
- 在導(dǎo)入應(yīng)用程序模塊的語句前使用
訪問控制語法
- 通過修飾符
open
、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() {}
- 除非已經(jīng)標(biāo)注,否則都會使用默認(rèn)的 internal 訪問級別
class SomeInternalClass {} // 隱式 internal
var someInternalConstant = 0 // 隱式 internal
自定義類型
- 一個類型的訪問級別會影響類型成員(屬性护戳、方法翎冲、構(gòu)造器、下標(biāo))的默認(rèn)訪問級別
- 將類型定為
private
或fileprivate
媳荒,該類型成員默認(rèn)訪問級別也變成private
或fileprivate
級別 -
類型指定為
internal
或public
(或者不明確指定訪問級別抗悍,而使用默認(rèn)的internal
),該類型所有成員的默認(rèn)訪問級別將是internal
一個
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
凝化。
元組不同于類、結(jié)構(gòu)體缀磕、枚舉缘圈、函數(shù)那樣有單獨的定義。
一個元組的訪問級別由元組中元素的訪問級別來決定的袜蚕,不能被顯式指定糟把。
函數(shù)類型
- 根據(jù)最嚴(yán)格的參數(shù)類型或返回類型的訪問級別來決定
- 如不符合函數(shù)定義所在環(huán)境的默認(rèn)訪問級別,需明確指定函數(shù)訪問級別
- 按下面這種寫法牲剃,代碼將無法通過編譯:
func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 此處是函數(shù)實現(xiàn)部分
}
- 返回類型-該元組的訪問級別是
private
- 必須使明確用
private
修飾符來明確指定該函數(shù)的訪問級別
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// 此處是函數(shù)實現(xiàn)部分
}
- 該函數(shù)當(dāng)做
public
或internal
級別來使用的話遣疯,可能會無法訪問private
級別的返回值
枚舉類型
- 成員的訪問級別和該枚舉類型相同
- 不能為枚舉成員單獨指定不同的訪問級別
public enum CompassPoint {
case north
case south
case east
case west
}
// CompassPoint 被明確指定為 public,那么它的成員 north凿傅、south缠犀、east、west 的訪問級別同樣也是 public:
原始值和關(guān)聯(lián)值
- 原始值聪舒、關(guān)聯(lián)值的類型的訪問級別至少不能低于枚舉類型的訪問級別
- 如不能在一個
internal
的枚舉中定義private
的原始值類型
嵌套類型 - Nested Types
- 嵌套類型的訪問級別 = 包含它的類型的訪問級別
- private 級別的類型中定義的嵌套類型自動為 private 級別
- fileprivate 級別的類型中定義的嵌套類型自動為 fileprivate 級別
- public 或 internal 級別的類型中定義的嵌套類型自動為 internal 級別
- 想讓嵌套類型是 public 級別的辨液,必須顯式指明為 public
子類
- 可繼承同一模塊中的所有訪問權(quán)限的類,也可繼承不同模塊被 open 修飾的類
- 子類不得高于父類(子類 ≤ 父類)
- 如箱残,父類是
internal
滔迈,子類不能是public
- 可重寫類成員(方法止吁,屬性,初始化器或下標(biāo))
- 如箱残,父類是
- 提高父類權(quán)限:對 someMethod() 函數(shù)進(jìn)行了重寫即改為“internal”級別燎悍,這比 someMethod() 的原本實現(xiàn)級別更高
public class A {
fileprivate func someMethod() {}
}
internal class B: A {
override internal func someMethod() {}
}
- 類 A 和子類 B 定義在同一個源文件中敬惦,那么 B 類可以在 someMethod() 中調(diào)用父類的 someMethod()
常量、變量谈山、屬性俄删、下標(biāo)
- 常量、變量奏路、屬性不能擁有比它們類型更高的訪問級別畴椰。
- 如,你不能寫一個public 的屬性而它的類型是 private 的
- 下標(biāo)也不能擁有比索引類型或返回類型更高的訪問級別
private var privateInstance = SomePrivateClass()
Getter 和 Setter
- getter 和 setter 和它們所屬常量思劳、變量迅矛、屬性和下標(biāo)的訪問級別相同
-
Setter
的訪問級別可低于Getter
,從而控制讀寫權(quán)限 - 語法:
var
或subscript
關(guān)鍵字之前潜叛,你可以通過fileprivate(set)
秽褒,private(set)
或internal(set)
為它們的寫入權(quán)限指定更低的訪問級別
這規(guī)則適用于存儲型和計算型屬性。
即使你不明確指定存儲型屬性的
Getter
和Setter
威兜,Swift 也會隱式創(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)的訪問級別 internal
-
Setter
的訪問級別是private
,這表示該屬性只能在內(nèi)部修改椒舵,而在結(jié)構(gòu)體的外部則表現(xiàn)為一個只讀屬性
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
屬性的值蚂踊,但不能對其賦值
- 將
TrackedString
結(jié)構(gòu)體明確為public
- 結(jié)構(gòu)體的成員(包括
numberOfEdits
屬性)擁有默認(rèn)的訪問級別internal
- 結(jié)合
public
和private(set)
修飾符- 把結(jié)構(gòu)體中的
numberOfEdits
屬性的Getter
的訪問級別設(shè)置為public
- 而
Setter
的訪問級別設(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)造器
- 可低于或等于所屬類型
- 必要構(gòu)造器
- 必須和所屬類型的訪問級別相同
- 類似函數(shù)或方法,構(gòu)造器參數(shù)不能低于構(gòu)造器本身的訪問級別
默認(rèn)構(gòu)造器
- Swift 會為結(jié)構(gòu)體和類提供一個默認(rèn)的無參數(shù)的構(gòu)造器(前提條件:給存儲屬性賦初值 + 未定義構(gòu)造器)
- 默認(rèn)構(gòu)造器的訪問級別與所屬類型的訪問級別相同
- 類型被指定為
public
級別笔宿,那么默認(rèn)構(gòu)造器的訪問級別將為internal
- 類型被指定為
- 希望在其他模塊中使用這種無參數(shù)的默認(rèn)構(gòu)造器犁钟,自己提供一個
public
訪問級別的無參數(shù)構(gòu)造器
結(jié)構(gòu)體默認(rèn)的成員逐一構(gòu)造器
- 任意存儲型屬性的訪問級別為
private
,成員逐一構(gòu)造器的訪問級別就是private
泼橘。否則涝动,這種構(gòu)造器的訪問級別依然是internal
。 - 希望一個
public
級別的結(jié)構(gòu)體也能在其他模塊中使用其默認(rèn)的成員逐一構(gòu)造器炬灭,只能自己提供一個public
訪問級別的成員逐一構(gòu)造器
協(xié)議
- 限制該協(xié)議只能在適當(dāng)?shù)脑L問級別范圍內(nèi)被遵循醋粟。
- 協(xié)議中的每個方法或?qū)傩远急仨毢驮搮f(xié)議相同的訪問級別
- 不能將協(xié)議中的方法或?qū)傩栽O(shè)置為其他訪問級別
- 才能確保該協(xié)議的所有方法或?qū)傩詫τ谌我庾裱叨伎捎谩?/li>
協(xié)議繼承
- 新協(xié)議和被繼承協(xié)議的訪問級別相同
- 如,不能將繼承自
internal
協(xié)議的新協(xié)議定為public
協(xié)議重归。
- 如,不能將繼承自
協(xié)議遵循
-
一個類型可遵循比它級別更低的協(xié)議
- 一個
public
級別類型米愿,如果遵循一個internal
協(xié)議,遵循的部分只能在這internal
協(xié)議所在的模塊中使用
- 一個
-
遵循了協(xié)議的類鼻吮,取協(xié)議和類的訪問級別的最小者
- 如類型是 public 育苟,遵循協(xié)議 internal 級別,這個類型就是 internal 級別的
-
寫或擴(kuò)展一個類型讓它遵循一個協(xié)議時椎木,類按協(xié)議要求的實現(xiàn)方法與該協(xié)議的訪問級別一致
- 一個
public
類型遵循一個internal
協(xié)議宙搬,這個類型對協(xié)議的所有實現(xiàn)至少都應(yīng)是internal
級別的
- 一個
Swift 和 Objective-C 一樣笨腥,協(xié)議遵循是全局的,也就是說勇垛,在同一程序中,一個類型不可能用兩種不同的方式實現(xiàn)同一個協(xié)議士鸥。
擴(kuò)展 - Extension
- Extension 的新增成員有和原始類型成員一致的訪問級別
- extension 一個
public
或者internal
類型闲孤, extension 中的成員默認(rèn)為internal
訪問級別 - 用 extension 擴(kuò)展一個
fileprivate
類型,則 extension 中的成員默認(rèn)使用fileprivate
訪問級別 - 用 extension 擴(kuò)展了一個
private
類型烤礁,則 extension 的成員默認(rèn)使用private
訪問級別
- extension 一個
- 可以重新指定 extension 的默認(rèn)訪問級別(例如讼积,
private
),從而給 extension 中所有成員一個新默認(rèn)訪問級別 -
用 extension 來遵循協(xié)議的話脚仔,就不能顯式地聲明 extension 的訪問級別
- extension 每個 protocol 要求的實現(xiàn)都默認(rèn)使用 protocol 的訪問級別
Extension 的私有成員
- 擴(kuò)展同一文件內(nèi)的類勤众,結(jié)構(gòu)體或者枚舉,extension 里的代碼會表現(xiàn)得跟聲明在原類型里的一模一樣鲤脏。也就是說你可以這樣:
- 在類型的聲明里们颜,聲明一個私有成員,在同一文件的 extension 里訪問猎醇。
- 在 extension 里聲明一個私有成員窥突,在同一文件的另一個 extension 里訪問。
- 在 extension 里聲明一個私有成員硫嘶,在同一文件的類型聲明里訪問阻问。
- 可以使用 extension 來組織你的代碼,而且不受私有成員的影響
protocol SomeProtocol {
func doSomething()
}
- 使用 extension 來遵循協(xié)議沦疾,就像這樣:
struct SomeStruct {
private var privateVariable = 12
}
extension SomeStruct: SomeProtocol {
func doSomething() {
print(privateVariable)
}
}
泛型
- 泛型類型或泛型函數(shù)称近,取決于泛型類型或泛型函數(shù)本身的訪問級別
- 還需結(jié)合類型參數(shù)的類型約束的訪問級別
- 根據(jù)這些訪問級別中的最低訪問級別來確定
類型別名
- 類型別名的訪問級別,不能高于原類型
-
private
級別的類型別名可以作為private
哮塞、fileprivate
刨秆、internal
、public
或者open
類型的別名 - 但是
public
級別的類型別名只能作為public
類型的別名彻桃,不能作為internal
坛善、fileprivate
或private
類型的別名。
-
這條規(guī)則也適用于為滿足協(xié)議遵循而將類型別名用于關(guān)聯(lián)類型的情況邻眷。