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

訪問(wèn)控制


1.0 翻譯:JaceFu 校對(duì):ChildhoodAndy

2.0 翻譯+校對(duì):mmoaay

2.1 翻譯:Prayer 校對(duì):shanks,2015-11-01

2.2 翻譯+校對(duì):SketchK 2016-05-17

3.0.1 翻譯+校對(duì): shanks,2016-11-13

4.0 翻譯:kemchenj,2017-09-23

本頁(yè)內(nèi)容包括:

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

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

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

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

模塊和源文件

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

模塊指的是獨(dú)立的代碼單元,框架或應(yīng)用程序會(huì)作為一個(gè)獨(dú)立的模塊來(lái)構(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 中的源代碼文件,它通常屬于一個(gè)模塊,即一個(gè)應(yīng)用程序或者框架。盡管我們一般會(huì)將不同的類(lèi)型分別定義在不同的源文件中,但是同一個(gè)源文件也可以包含多個(gè)類(lèi)型、函數(shù)之類(lèi)的定義。

訪問(wèn)級(jí)別

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

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

Open 為最高訪問(wèn)級(jí)別(限制最少),Private 為最低訪問(wèn)級(jí)別(限制最多)。

Open 只能作用于類(lèi)和類(lèi)的成員,它和 Public 的區(qū)別如下:

  • Public 或者其它更嚴(yán)訪問(wèn)級(jí)別的類(lèi),只能在其定義的模塊內(nèi)部被繼承。
  • Public 或者其它更嚴(yán)訪問(wèn)級(jí)別的類(lèi)成員,只能在其定義的模塊內(nèi)部的子類(lèi)中重寫(xiě)。
  • Open 的類(lèi),可以在其定義的模塊中被繼承,也可以在引用它的模塊中被繼承。
  • Open 的類(lèi)成員,可以在其定義的模塊中子類(lèi)中重寫(xiě),也可以在引用它的模塊中的子類(lèi)重寫(xiě)。

把一個(gè)類(lèi)標(biāo)記為 open,明確的表示你已經(jīng)充分考慮過(guò)外部模塊使用此類(lèi)作為父類(lèi)的影響,并且設(shè)計(jì)好了你的類(lèi)的代碼了。

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

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

例如:

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

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

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

如果你沒(méi)有為代碼中的實(shí)體顯式指定訪問(wèn)級(jí)別,那么它們默認(rèn)為 internal 級(jí)別(有一些例外情況,稍后會(huì)進(jìn)行說(shuō)明)。因此,在大多數(shù)情況下,我們不需要顯式指定實(shí)體的訪問(wèn)級(jí)別。

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

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

框架的訪問(wèn)級(jí)別

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

注意 框架依然會(huì)使用默認(rèn)的 internal ,也可以指定為 fileprivate 訪問(wèn)或者 private 訪問(wèn)級(jí)別。當(dāng)你想把某個(gè)實(shí)體作為框架的 API 的時(shí)候,需顯式為其指定開(kāi)放訪問(wèn)或公開(kāi)訪問(wèn)級(jí)別。

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

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

訪問(wèn)控制語(yǔ)法

通過(guò)修飾符 openpublic,internal,fileprivateprivate 來(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() {}

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

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

自定義類(lèi)型

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

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

重要
上面提到,一個(gè) public 類(lèi)型的所有成員的訪問(wèn)級(jí)別默認(rèn)為 internal 級(jí)別,而不是 public 級(jí)別。如果你想將某個(gè)成員指定為 public 級(jí)別,那么你必須顯式指定。這樣做的好處是,在你定義公共接口的時(shí)候,可以明確地選擇哪些接口是需要公開(kāi)的,哪些是內(nè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)型

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

注意
元組不同于類(lèi)、結(jié)構(gòu)體、枚舉、函數(shù)那樣有單獨(dú)的定義。元組的訪問(wèn)級(jí)別是在它被使用時(shí)自動(dòng)推斷出的,而無(wú)法明確指定。

函數(shù)類(lèi)型

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

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

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

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

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

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

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

枚舉類(lèi)型

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

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

public enum CompassPoint {
       case North
       case South
       case East
       case West
}

原始值和關(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)型

如果在 private 的類(lèi)型中定義嵌套類(lèi)型,那么該嵌套類(lèi)型就自動(dòng)擁有 private 訪問(wèn)級(jí)別。如果在 public 或者 internal 級(jí)別的類(lèi)型中定義嵌套類(lèi)型,那么該嵌套類(lèi)型自動(dòng)擁有 internal 訪問(wèn)級(jí)別。如果想讓嵌套類(lèi)型擁有 public 訪問(wèn)級(jí)別,那么需要明確指定該嵌套類(lèi)型的訪問(wèn)級(jí)別。

子類(lèi)

子類(lèi)的訪問(wèn)級(jí)別不得高于父類(lèi)的訪問(wèn)級(jí)別。例如,父類(lèi)的訪問(wèn)級(jí)別是 internal,子類(lèi)的訪問(wèn)級(jí)別就不能是 public。

此外,你可以在符合當(dāng)前訪問(wèn)級(jí)別的條件下重寫(xiě)任意類(lèi)成員(方法、屬性、構(gòu)造器、下標(biāo)等)。

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

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

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

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

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

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

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

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

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

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

private var privateInstance = SomePrivateClass()

Getter 和 Setter

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

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

注意
這個(gè)規(guī)則同時(shí)適用于存儲(chǔ)型屬性和計(jì)算型屬性。即使你不明確指定存儲(chǔ)型屬性的 GetterSetter,Swift 也會(huì)隱式地為其創(chuàng)建 GetterSetter,用于訪問(wèn)該屬性的后備存儲(chǔ)。使用 fileprivate(set),private(set)internal(set) 可以改變 Setter 的訪問(wèn)級(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è)功能通過(guò)屬性 valuedidSet 觀察器實(shí)現(xiàn),每當(dāng)給 value 賦新值時(shí)就會(huì)調(diào)用 didSet 方法,然后將 numberOfEdits 的值加一。

結(jié)構(gòu)體 TrackedString 和它的屬性 value 都沒(méi)有顯式地指定訪問(wèn)級(jí)別,所以它們都是用默認(rèn)的訪問(wèn)級(jí)別 internal。但是該結(jié)構(gòu)體的 numberOfEdits 屬性使用了 private(set) 修飾符,這意味著 numberOfEdits 屬性只能在結(jié)構(gòu)體的定義中進(jìn)行賦值。numberOfEdits 屬性的 Getter 依然是默認(rèn)的訪問(wèn)級(jí)別 internal,但是 Setter 的訪問(wèn)級(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í)還提供了方便的訪問(wèn)方式。

你可以在必要時(shí)為 GetterSetter 顯式指定訪問(wèn)級(jí)別。下面的例子將 TrackedString 結(jié)構(gòu)體明確指定為了 public 訪問(wèn)級(jí)別。結(jié)構(gòu)體的成員(包括 numberOfEdits 屬性)擁有默認(rèn)的訪問(wèn)級(jí)別 internal。你可以結(jié)合 publicprivate(set) 修飾符把結(jié)構(gòu)體中的 numberOfEdits 屬性的 Getter 的訪問(wèn)級(jí)別設(shè)置為 public,而 Setter 的訪問(wèn)級(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)造器的訪問(wèn)級(jí)別可以低于或等于其所屬類(lèi)型的訪問(wèn)級(jí)別。唯一的例外是必要構(gòu)造器,它的訪問(wèn)級(jí)別必須和所屬類(lèi)型的訪問(wèn)級(jí)別相同。

如同函數(shù)或方法的參數(shù),構(gòu)造器參數(shù)的訪問(wèn)級(jí)別也不能低于構(gòu)造器本身的訪問(wèn)級(jí)別。

默認(rèn)構(gòu)造器

默認(rèn)構(gòu)造器所述,Swift 會(huì)為結(jié)構(gòu)體和類(lèi)提供一個(gè)默認(rèn)的無(wú)參數(shù)的構(gòu)造器,只要它們?yōu)樗写鎯?chǔ)型屬性設(shè)置了默認(rèn)初始值,并且未提供自定義的構(gòu)造器。

默認(rèn)構(gòu)造器的訪問(wèn)級(jí)別與所屬類(lèi)型的訪問(wèn)級(jí)別相同,除非類(lèi)型的訪問(wèn)級(jí)別是 public。如果一個(gè)類(lèi)型被指定為 public 級(jí)別,那么默認(rèn)構(gòu)造器的訪問(wèn)級(jí)別將為 internal。如果你希望一個(gè) public 級(jí)別的類(lèi)型也能在其他模塊中使用這種無(wú)參數(shù)的默認(rèn)構(gòu)造器,你只能自己提供一個(gè) public 訪問(wèn)級(jí)別的無(wú)參數(shù)構(gòu)造器。

結(jié)構(gòu)體默認(rèn)的成員逐一構(gòu)造器

如果結(jié)構(gòu)體中任意存儲(chǔ)型屬性的訪問(wèn)級(jí)別為 private,那么該結(jié)構(gòu)體默認(rèn)的成員逐一構(gòu)造器的訪問(wèn)級(jí)別就是 private。否則,這種構(gòu)造器的訪問(wèn)級(jí)別依然是 internal。

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

協(xié)議

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

協(xié)議中的每一個(gè)要求都具有和該協(xié)議相同的訪問(wèn)級(jí)別。你不能將協(xié)議中的要求設(shè)置為其他訪問(wèn)級(jí)別。這樣才能確保該協(xié)議的所有要求對(duì)于任意采納者都將可用。

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

協(xié)議繼承

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

協(xié)議一致性

一個(gè)類(lèi)型可以采納比自身訪問(wèn)級(jí)別低的協(xié)議。例如,你可以定義一個(gè) public 級(jí)別的類(lèi)型,它可以在其他模塊中使用,同時(shí)它也可以采納一個(gè) internal 級(jí)別的協(xié)議,但是只能在該協(xié)議所在的模塊中作為符合該協(xié)議的類(lèi)型使用。

采納了協(xié)議的類(lèi)型的訪問(wèn)級(jí)別取它本身和所采納協(xié)議兩者間最低的訪問(wèn)級(jí)別。也就是說(shuō)如果一個(gè)類(lèi)型是 public 級(jí)別,采納的協(xié)議是 internal 級(jí)別,那么采納了這個(gè)協(xié)議后,該類(lèi)型作為符合協(xié)議的類(lèi)型時(shí),其訪問(wèn)級(jí)別也是 internal。

如果你采納了協(xié)議,那么實(shí)現(xiàn)了協(xié)議的所有要求后,你必須確保這些實(shí)現(xiàn)的訪問(wèn)級(jí)別不能低于協(xié)議的訪問(wèn)級(jí)別。例如,一個(gè) public 級(jí)別的類(lèi)型,采納了 internal 級(jí)別的協(xié)議,那么協(xié)議的實(shí)現(xiàn)至少也得是 internal 級(jí)別。

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

Extension

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

或者,你可以明確指定 extension 的訪問(wèn)級(jí)別(例如,private extension),從而給該 extension 中的所有成員指定一個(gè)新的默認(rèn)訪問(wèn)級(jí)別。這個(gè)新的默認(rèn)訪問(wèn)級(jí)別仍然可以被單獨(dú)指定的訪問(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,而且不受私有成員的影響。例如,給定下面這樣一個(gè)簡(jiǎn)單的協(xié)議:

protocol SomeProtocol {
    func doSomething() {}
}

你可以使用 extension 來(lái)遵守協(xié)議,就想這樣:

struct SomeStruct {
    private var privateVariable = 12
}

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

泛型

泛型類(lèi)型或泛型函數(shù)的訪問(wèn)級(jí)別取決于泛型類(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)型別名都會(huì)被當(dāng)作不同的類(lèi)型,以便于進(jìn)行訪問(wèn)控制。類(lèi)型別名的訪問(wèn)級(jí)別不可高于其表示的類(lèi)型的訪問(wèn)級(jí)別。例如,private 級(jí)別的類(lèi)型別名可以作為 privatefile-private,internal,public或者open類(lèi)型的別名,但是 public 級(jí)別的類(lèi)型別名只能作為 public 類(lèi)型的別名,不能作為 internalfile-private,或 private 類(lèi)型的別名。

注意
這條規(guī)則也適用于為滿足協(xié)議一致性而將類(lèi)型別名用于關(guān)聯(lián)類(lèi)型的情況。

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