這里的教程為Swift官方教程中文版。

訪問控制

訪問控制可以限定其它源文件或模塊對(duì)你的代碼的訪問。這個(gè)特性可以讓你隱藏代碼的實(shí)現(xiàn)細(xì)節(jié),并且能提供一個(gè)接口來讓別人訪問和使用你的代碼。

你可以明確地給單個(gè)類型(類、結(jié)構(gòu)體、枚舉)設(shè)置訪問級(jí)別,也可以給這些類型的屬性、方法、構(gòu)造器、下標(biāo)等設(shè)置訪問級(jí)別。協(xié)議也可以被限定在一定訪問級(jí)別的范圍內(nèi)使用,包括協(xié)議里的全局常量、變量和函數(shù)。

Swift 不僅提供了多種不同的訪問級(jí)別,還為某些典型場(chǎng)景提供了默認(rèn)的訪問級(jí)別,這樣就不需要我們?cè)诿慷未a中都顯式聲明訪問級(jí)別。如果你只是開發(fā)一個(gè)單 target 的應(yīng)用程序,完全可以不用顯式聲明代碼的訪問級(jí)別。

注意

為了簡單起見,對(duì)于代碼中可以設(shè)置訪問級(jí)別的特性(屬性、基本類型、函數(shù)等),在下面的章節(jié)中我們會(huì)統(tǒng)一稱之為“實(shí)體”。

模塊和源文件

Swift 中的訪問控制模型基于模塊和源文件這兩個(gè)概念。

模塊指的是獨(dú)立的代碼單元,框架或應(yīng)用程序會(huì)作為一個(gè)獨(dú)立的模塊來構(gòu)建和發(fā)布。在 Swift 中,一個(gè)模塊可以使用 import 關(guān)鍵字導(dǎo)入另外一個(gè)模塊。

在 Swift 中,Xcode 的每個(gè) target(例如框架或應(yīng)用程序)都被當(dāng)作獨(dú)立的模塊處理。如果你是為了實(shí)現(xiàn)某個(gè)通用的功能,或者是為了封裝一些常用方法而將代碼打包成獨(dú)立的框架,這個(gè)框架就是 Swift 中的一個(gè)模塊。當(dāng)它被導(dǎo)入到某個(gè)應(yīng)用程序或者其他框架時(shí),框架的內(nèi)容都將屬于這個(gè)獨(dú)立的模塊。

源文件 就是 Swift 模塊中的源代碼文件(實(shí)際上,源文件屬于一個(gè)應(yīng)用程序或框架)。盡管我們一般會(huì)將不同的類型分別定義在不同的源文件中,但是同一個(gè)源文件也可以包含多個(gè)類型、函數(shù)等的定義。

訪問級(jí)別

Swift 為代碼中的實(shí)體提供了五種不同的訪問級(jí)別。這些訪問級(jí)別不僅與源文件中定義的實(shí)體相關(guān),同時(shí)也與源文件所屬的模塊相關(guān)。

  • openpublic 級(jí)別可以讓實(shí)體被同一模塊源文件中的所有實(shí)體訪問,在模塊外也可以通過導(dǎo)入該模塊來訪問源文件里的所有實(shí)體。通常情況下,你會(huì)使用 open 或 public 級(jí)別來指定框架的外部接口。open 和 public 的區(qū)別在后面會(huì)提到。
  • internal 級(jí)別讓實(shí)體被同一模塊源文件中的任何實(shí)體訪問,但是不能被模塊外的實(shí)體訪問。通常情況下,如果某個(gè)接口只在應(yīng)用程序或框架內(nèi)部使用,就可以將其設(shè)置為 internal 級(jí)別。
  • fileprivate 限制實(shí)體只能在其定義的文件內(nèi)部訪問。如果功能的部分實(shí)現(xiàn)細(xì)節(jié)只需要在文件內(nèi)使用時(shí),可以使用 fileprivate 來將其隱藏。
  • private 限制實(shí)體只能在其定義的作用域,以及同一文件內(nèi)的 extension 訪問。如果功能的部分細(xì)節(jié)只需要在當(dāng)前作用域內(nèi)使用時(shí),可以使用 private 來將其隱藏。

open 為最高訪問級(jí)別(限制最少),private 為最低訪問級(jí)別(限制最多)。

open 只能作用于類和類的成員,它和 public 的區(qū)別主要在于 open 限定的類和成員能夠在模塊外能被繼承和重寫,在下面的 子類 這一節(jié)中有詳解。將類的訪問級(jí)別顯示指定為 open 表明你已經(jīng)設(shè)計(jì)好了類的代碼,并且充分考慮過這個(gè)類在其他模塊中用作父類時(shí)的影響。

訪問級(jí)別基本原則

Swift 中的訪問級(jí)別遵循一個(gè)基本原則:實(shí)體不能定義在具有更低訪問級(jí)別(更嚴(yán)格)的實(shí)體中。

例如:

  • 一個(gè) public 的變量,其類型的訪問級(jí)別不能是 internal,fileprivate 或是 private。因?yàn)闊o法保證變量的類型在使用變量的地方也具有訪問權(quán)限。
  • 函數(shù)的訪問級(jí)別不能高于它的參數(shù)類型和返回類型的訪問級(jí)別。因?yàn)檫@樣就會(huì)出現(xiàn)函數(shù)可以在任何地方被訪問,但是它的參數(shù)類型和返回類型卻不可以的情況。

關(guān)于此原則在各種情況下的具體表現(xiàn),將在下文有所體現(xiàn)。

默認(rèn)訪問級(jí)別

你代碼中所有的實(shí)體,如果你不顯式的指定它們的訪問級(jí)別,那么它們將都有一個(gè) internal 的默認(rèn)訪問級(jí)別,(有一些例外情況,本文稍后會(huì)有說明)。因此,多數(shù)情況下你不需要顯示指定實(shí)體的訪問級(jí)別。

單 target 應(yīng)用程序的訪問級(jí)別

當(dāng)你編寫一個(gè)單 target 應(yīng)用程序時(shí),應(yīng)用的所有功能都是為該應(yīng)用服務(wù),而不需要提供給其他應(yīng)用或者模塊使用,所以你不需要明確設(shè)置訪問級(jí)別,使用默認(rèn)的訪問級(jí)別 internal 即可。但是,你也可以使用 fileprivateprivate 訪問級(jí)別,用于隱藏一些功能的實(shí)現(xiàn)細(xì)節(jié)。

框架的訪問級(jí)別

當(dāng)你開發(fā)框架時(shí),就需要把一些對(duì)外的接口定義為 open 或 public 訪問級(jí)別,以便使用者導(dǎo)入該框架后可以正常使用其功能。這些被你定義為對(duì)外的接口,就是這個(gè)框架的 API。

注意

框架的內(nèi)部實(shí)現(xiàn)仍然可以使用默認(rèn)的訪問級(jí)別 internal,當(dāng)你需要對(duì)框架內(nèi)部其它部分隱藏細(xì)節(jié)時(shí)可以使用 privatefileprivate。對(duì)于框架的對(duì)外 API 部分,你就需要將它們?cè)O(shè)置為 openpublic 了。

單元測(cè)試 target 的訪問級(jí)別

當(dāng)你的應(yīng)用程序包含單元測(cè)試 target 時(shí),為了測(cè)試,測(cè)試模塊需要訪問應(yīng)用程序模塊中的代碼。默認(rèn)情況下只有 openpublic 級(jí)別的實(shí)體才可以被其他模塊訪問。然而,如果在導(dǎo)入應(yīng)用程序模塊的語句前使用 @testable 特性,然后在允許測(cè)試的編譯設(shè)置(Build Options -> Enable Testability)下編譯這個(gè)應(yīng)用程序模塊,單元測(cè)試目標(biāo)就可以訪問應(yīng)用程序模塊中所有內(nèi)部級(jí)別的實(shí)體。

訪問控制語法

通過修飾符 open、public、internal、fileprivate、private 來聲明實(shí)體的訪問級(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() {}

除非專門指定,否則實(shí)體默認(rèn)的訪問級(jí)別為 internal,可以查閱 默認(rèn)訪問級(jí)別 這一節(jié)。這意味著在不使用修飾符顯式聲明訪問級(jí)別的情況下,SomeInternalClasssomeInternalConstant 的訪問級(jí)別是 internal

class SomeInternalClass {}   // 隱式 internal
var someInternalConstant = 0 // 隱式 internal

自定義類型

如果想為一個(gè)自定義類型指定訪問級(jí)別,在定義類型時(shí)進(jìn)行指定即可。新類型只能在它的訪問級(jí)別限制范圍內(nèi)使用。例如,你定義了一個(gè) fileprivate 級(jí)別的類,那這個(gè)類就只能在定義它的源文件中使用,可以作為屬性類型、函數(shù)參數(shù)類型或者返回類型等等。

一個(gè)類型的訪問級(jí)別也會(huì)影響到類型成員(屬性、方法、構(gòu)造器、下標(biāo))的默認(rèn)訪問級(jí)別。如果你將類型指定為 private 或者 fileprivate 級(jí)別,那么該類型的所有成員的默認(rèn)訪問級(jí)別也會(huì)變成 private 或者 fileprivate 級(jí)別。如果你將類型指定為 internalpublic(或者不明確指定訪問級(jí)別,而使用默認(rèn)的 internal ),那么該類型的所有成員的默認(rèn)訪問級(jí)別將是 internal。

重點(diǎn)

上面提到,一個(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 類成員
    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 類成員
}

元組類型

元組的訪問級(jí)別將由元組中訪問級(jí)別最嚴(yán)格的類型來決定。例如,如果你構(gòu)建了一個(gè)包含兩種不同類型的元組,其中一個(gè)類型為 internal,另一個(gè)類型為 private,那么這個(gè)元組的訪問級(jí)別為 private。

注意

元組不同于類、結(jié)構(gòu)體、枚舉、函數(shù)那樣有單獨(dú)的定義。一個(gè)元組的訪問級(jí)別由元組中元素的訪問級(jí)別來決定的,不能被顯示指定。

函數(shù)類型

函數(shù)的訪問級(jí)別根據(jù)訪問級(jí)別最嚴(yán)格的參數(shù)類型或返回類型的訪問級(jí)別來決定。但是,如果這種訪問級(jí)別不符合函數(shù)定義所在環(huán)境的默認(rèn)訪問級(jí)別,那么就需要明確地指定該函數(shù)的訪問級(jí)別。

下面的例子定義了一個(gè)名為 someFunction() 的全局函數(shù),并且沒有明確地指定其訪問級(jí)別。也許你會(huì)認(rèn)為該函數(shù)應(yīng)該擁有默認(rèn)的訪問級(jí)別 internal,但事實(shí)并非如此。事實(shí)上,如果按下面這種寫法,代碼將無法通過編譯:

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 此處是函數(shù)實(shí)現(xiàn)部分
}

我們可以看到,這個(gè)函數(shù)的返回類型是一個(gè)元組,該元組中包含兩個(gè)自定義的類(可查閱 自定義類型)。其中一個(gè)類的訪問級(jí)別是 internal,另一個(gè)的訪問級(jí)別是 private,所以根據(jù)元組訪問級(jí)別的原則,該元組的訪問級(jí)別是 private(元組的訪問級(jí)別與元組中訪問級(jí)別最低的類型一致)。

因?yàn)樵摵瘮?shù)返回類型的訪問級(jí)別是 private,所以你必須使用 private 修飾符來明確指定該函數(shù)的訪問級(jí)別:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 此處是函數(shù)實(shí)現(xiàn)部分
}

將該函數(shù)指定為 publicinternal,或者使用默認(rèn)的訪問級(jí)別 internal 都是錯(cuò)誤的,因?yàn)槿绻言摵瘮?shù)當(dāng)做 publicinternal 級(jí)別來使用的話,可能會(huì)無法訪問 private 級(jí)別的返回值。

枚舉類型

枚舉成員的訪問級(jí)別和該枚舉類型相同,你不能為枚舉成員單獨(dú)指定不同的訪問級(jí)別。

比如下面的例子,枚舉 CompassPoint 被明確指定為 public,那么它的成員 north、southeastwest 的訪問級(jí)別同樣也是 public

public enum CompassPoint {
    case north
    case south
    case east
    case west
}

原始值和關(guān)聯(lián)值

枚舉定義中的任何原始值或關(guān)聯(lián)值的類型的訪問級(jí)別至少不能低于枚舉類型的訪問級(jí)別。例如,你不能在一個(gè) internal 的枚舉中定義 private 的原始值類型。

嵌套類型

嵌套類型的訪問級(jí)別和包含它的類型的訪問級(jí)別相同,嵌套類型是 public 的情況除外。在一個(gè) public 的類型中定義嵌套類型,那么嵌套類型自動(dòng)擁有 internal 的訪問級(jí)別。如果你想讓嵌套類型擁有 public 訪問級(jí)別,那么必須顯式指定該嵌套類型的訪問級(jí)別為 public。

子類

你可以繼承同一模塊中的所有有訪問權(quán)限的類,也可以繼承不同模塊中被 open 修飾的類。一個(gè)子類的訪問級(jí)別不得高于父類的訪問級(jí)別。例如,父類的訪問級(jí)別是 internal,子類的訪問級(jí)別就不能是 public。

此外,在同一模塊中,你可以在符合當(dāng)前訪問級(jí)別的條件下重寫任意類成員(方法、屬性、構(gòu)造器、下標(biāo)等)。在不同模塊中,你可以重寫類中被 open 修飾的成員。

可以通過重寫給所繼承類的成員提供更高的訪問級(jí)別。下面的例子中,類 A 的訪問級(jí)別是 public,它包含一個(gè)方法 someMethod(),訪問級(jí)別為 fileprivate。類 B 繼承自類 A,訪問級(jí)別為 internal,但是在類 B 中重寫了類 A 中訪問級(jí)別為 fileprivate 的方法 someMethod(),并重新指定為 internal 級(jí)別。通過這種方式,我們就可以將某類中 fileprivate 級(jí)別的類成員重新指定為更高的訪問級(jí)別,以便其他人使用:

public class A {
    fileprivate func someMethod() {}
}

internal class B: A {
    override internal func someMethod() {}
}

我們甚至可以在子類中,用子類成員去訪問訪問級(jí)別更低的父類成員,只要這一操作在相應(yīng)訪問級(jí)別的限制范圍內(nèi)(也就是說,在同一源文件中訪問父類 fileprivate 級(jí)別的成員,在同一模塊內(nèi)訪問父類 internal 級(jí)別的成員):

public class A {
    fileprivate func someMethod() {}
}

internal class B: A {
    override internal func someMethod() {
        super.someMethod()
    }
}

因?yàn)楦割?A 和子類 B 定義在同一個(gè)源文件中,所以在子類 B 可以在重寫的 someMethod() 方法中調(diào)用 super.someMethod()

常量、變量、屬性、下標(biāo)

常量、變量、屬性不能擁有比它們的類型更高的訪問級(jí)別。例如,你不能定義一個(gè) public 級(jí)別的屬性,但是它的類型卻是 private 級(jí)別的。同樣,下標(biāo)也不能擁有比索引類型或返回類型更高的訪問級(jí)別。

如果常量、變量、屬性、下標(biāo)的類型是 private 級(jí)別的,那么它們必須明確指定訪問級(jí)別為 private

private var privateInstance = SomePrivateClass()

Getter 和 Setter

常量、變量、屬性、下標(biāo)的 GettersSetters 的訪問級(jí)別和它們所屬類型的訪問級(jí)別相同。

Setter 的訪問級(jí)別可以低于對(duì)應(yīng)的 Getter 的訪問級(jí)別,這樣就可以控制變量、屬性或下標(biāo)的讀寫權(quán)限。在 varsubscript 關(guān)鍵字之前,你可以通過 fileprivate(set),private(set)internal(set) 為它們的寫入權(quán)限指定更低的訪問級(jí)別。

注意

這個(gè)規(guī)則同時(shí)適用于存儲(chǔ)型屬性和計(jì)算型屬性。即使你不明確指定存儲(chǔ)型屬性的 GetterSetter,Swift 也會(huì)隱式地為其創(chuàng)建 GetterSetter,用于訪問該屬性的存儲(chǔ)內(nèi)容。使用 fileprivate(set),private(set)internal(set) 可以改變 Setter 的訪問級(jí)別,這對(duì)計(jì)算型屬性也同樣適用。

下面的例子中定義了一個(gè)名為 TrackedString 的結(jié)構(gòu)體,它記錄了 value 屬性被修改的次數(shù):

struct TrackedString {
    private(set) var numberOfEdits = 0
    var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
}

TrackedString 結(jié)構(gòu)體定義了一個(gè)用于存儲(chǔ) String 值的屬性 value,并將初始值設(shè)為 ""(一個(gè)空字符串)。該結(jié)構(gòu)體還定義了另一個(gè)用于存儲(chǔ) Int 值的屬性 numberOfEdits,它用于記錄屬性 value 被修改的次數(shù)。這個(gè)功能通過屬性 valuedidSet 觀察器實(shí)現(xiàn),每當(dāng)給 value 賦新值時(shí)就會(huì)調(diào)用 didSet 方法,然后將 numberOfEdits 的值加一。

結(jié)構(gòu)體 TrackedString 和它的屬性 value 都沒有顯式地指定訪問級(jí)別,所以它們都是用默認(rèn)的訪問級(jí)別 internal。但是該結(jié)構(gòu)體的 numberOfEdits 屬性使用了 private(set) 修飾符,這意味著 numberOfEdits 屬性只能在結(jié)構(gòu)體的定義中進(jìn)行賦值。numberOfEdits 屬性的 Getter 依然是默認(rèn)的訪問級(jí)別 internal,但是 Setter 的訪問級(jí)別是 private,這表示該屬性只能在內(nèi)部修改,而在結(jié)構(gòu)體的外部則表現(xiàn)為一個(gè)只讀屬性。

如果你實(shí)例化 TrackedString 結(jié)構(gòu)體,并多次對(duì) value 屬性的值進(jìn)行修改,你就會(huì)看到 numberOfEdits 的值會(huì)隨著修改次數(shù)而變化:

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”

雖然你可以在其他的源文件中實(shí)例化該結(jié)構(gòu)體并且獲取到 numberOfEdits 屬性的值,但是你不能對(duì)其進(jìn)行賦值。這一限制保護(hù)了該記錄功能的實(shí)現(xiàn)細(xì)節(jié),同時(shí)還提供了方便的訪問方式。

你可以在必要時(shí)為 GetterSetter 顯式指定訪問級(jí)別。下面的例子將 TrackedString 結(jié)構(gòu)體明確指定為了 public 訪問級(jí)別。結(jié)構(gòu)體的成員(包括 numberOfEdits 屬性)擁有默認(rèn)的訪問級(jí)別 internal。你可以結(jié)合 publicprivate(set) 修飾符把結(jié)構(gòu)體中的 numberOfEdits 屬性的 Getter 的訪問級(jí)別設(shè)置為 public,而 Setter 的訪問級(jí)別設(shè)置為 private

public struct TrackedString {
    public private(set) var numberOfEdits = 0
    public var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
    public init() {}
}

構(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)造器 所述,Swift 會(huì)為結(jié)構(gòu)體和類提供一個(gè)默認(rèn)的無參數(shù)的構(gòu)造器,只要它們?yōu)樗写鎯?chǔ)型屬性設(shè)置了默認(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。

如同前面提到的默認(rèn)構(gòu)造器,如果你希望一個(gè) public 級(jí)別的結(jié)構(gòu)體也能在其他模塊中使用其默認(rèn)的成員逐一構(gòu)造器,你依然只能自己提供一個(gè) public 訪問級(jí)別的成員逐一構(gòu)造器。

協(xié)議

如果想為一個(gè)協(xié)議類型明確地指定訪問級(jí)別,在聲明協(xié)議時(shí)指定即可。這將限制該協(xié)議只能在適當(dāng)?shù)脑L問級(jí)別范圍內(nèi)被遵循。

協(xié)議中的每個(gè)方法或?qū)傩远急仨毦哂泻驮搮f(xié)議相同的訪問級(jí)別。你不能將協(xié)議中的方法或?qū)傩栽O(shè)置為其他訪問級(jí)別。這樣才能確保該協(xié)議的所有方法或?qū)傩詫?duì)于任意遵循者都可用。

注意

如果你定義了一個(gè) public 訪問級(jí)別的協(xié)議,那么該協(xié)議的所有實(shí)現(xiàn)也會(huì)是 public 訪問級(jí)別。這一點(diǎn)不同于其他類型,例如,類型是 public 訪問級(jí)別時(shí),其成員的訪問級(jí)別卻只是 internal。

協(xié)議繼承

如果定義了一個(gè)繼承自其他協(xié)議的新協(xié)議,那么新協(xié)議擁有的訪問級(jí)別最高也只能和被繼承協(xié)議的訪問級(jí)別相同。例如,你不能將繼承自 internal 協(xié)議的新協(xié)議訪問級(jí)別指定為 public 協(xié)議。

協(xié)議遵循

一個(gè)類型可以遵循比它級(jí)別更低的協(xié)議。例如,你可以定義一個(gè) public 級(jí)別類型,它能在別的模塊中使用,但是如果它遵循一個(gè) internal 協(xié)議,這個(gè)遵循的部分就只能在這個(gè) internal 協(xié)議所在的模塊中使用。

遵循協(xié)議時(shí)的上下文級(jí)別是類型和協(xié)議中級(jí)別最小的那個(gè)。如果一個(gè)類型是 public 級(jí)別,但它要遵循的協(xié)議是 internal 級(jí)別,那么這個(gè)類型對(duì)該協(xié)議的遵循上下文就是 internal 級(jí)別。

當(dāng)你編寫或擴(kuò)展一個(gè)類型讓它遵循一個(gè)協(xié)議時(shí),你必須確保該類型對(duì)協(xié)議的每一個(gè)要求的實(shí)現(xiàn),至少與遵循協(xié)議的上下文級(jí)別一致。例如,一個(gè) public 類型遵循一個(gè) internal 協(xié)議,這個(gè)類型對(duì)協(xié)議的所有實(shí)現(xiàn)至少都應(yīng)是 internal 級(jí)別的。

注意

Swift 和 Objective-C 一樣,協(xié)議遵循是全局的,也就是說,在同一程序中,一個(gè)類型不可能用兩種不同的方式實(shí)現(xiàn)同一個(gè)協(xié)議。

Extension

Extension 可以在訪問級(jí)別允許的情況下對(duì)類、結(jié)構(gòu)體、枚舉進(jìn)行擴(kuò)展。Extension 的新增成員具有和原始類型成員一致的訪問級(jí)別。例如,你使用 extension 擴(kuò)展了一個(gè) public 或者 internal 類型,則 extension 中的成員就默認(rèn)使用 internal 訪問級(jí)別。如果你使用 extension 擴(kuò)展一個(gè) fileprivate 類型,則 extension 中的成員默認(rèn)使用 fileprivate 訪問級(jí)別。如果你使用 extension 擴(kuò)展了一個(gè) private 類型,則 extension 的成員默認(rèn)使用 private 訪問級(jí)別。

或者,你可以通過修飾語重新指定 extension 的默認(rèn)訪問級(jí)別(例如,private),從而給該 extension 中的所有成員指定一個(gè)新的默認(rèn)訪問級(jí)別。這個(gè)新的默認(rèn)訪問級(jí)別仍然可以被單獨(dú)成員指定的訪問級(jí)別所覆蓋。

如果你使用 extension 來遵循協(xié)議的話,就不能顯式地聲明 extension 的訪問級(jí)別。extension 每個(gè) protocol 要求的實(shí)現(xiàn)都默認(rèn)使用 protocol 的訪問級(jí)別。

Extension 的私有成員

擴(kuò)展同一文件內(nèi)的類,結(jié)構(gòu)體或者枚舉,extension 里的代碼會(huì)表現(xiàn)得跟聲明在原類型里的一模一樣。也就是說你可以這樣:

  • 在類型的聲明里聲明一個(gè)私有成員,在同一文件的 extension 里訪問。
  • 在 extension 里聲明一個(gè)私有成員,在同一文件的另一個(gè) extension 里訪問。
  • 在 extension 里聲明一個(gè)私有成員,在同一文件的類型聲明里訪問。

這意味著你可以使用 extension 來組織你的代碼,而且不受私有成員的影響。例如,給定下面這樣一個(gè)簡單的協(xié)議:

protocol SomeProtocol {
    func doSomething()
}

你可以使用 extension 來遵循協(xié)議,就像這樣:

struct SomeStruct {
    private var privateVariable = 12
}

extension SomeStruct: SomeProtocol {
    func doSomething() {
        print(privateVariable)
    }
}

泛型

泛型類型或泛型函數(shù)的訪問級(jí)別取決于泛型類型或泛型函數(shù)本身的訪問級(jí)別,還需結(jié)合類型參數(shù)的類型約束的訪問級(jí)別,根據(jù)這些訪問級(jí)別中的最低訪問級(jí)別來確定。

類型別名

你定義的任何類型別名都會(huì)被當(dāng)作不同的類型,以便于進(jìn)行訪問控制。類型別名的訪問級(jí)別不可高于其表示的類型的訪問級(jí)別。例如,private 級(jí)別的類型別名可以作為 privatefileprivate、internal、public 或者 open 類型的別名,但是 public 級(jí)別的類型別名只能作為 public 類型的別名,不能作為 internal、fileprivateprivate 類型的別名。

注意

這條規(guī)則也適用于為滿足協(xié)議遵循而將類型別名用于關(guān)聯(lián)類型的情況。

? 內(nèi)存安全 高級(jí)運(yùn)算符 ?
?