【Swift 3.1】24 -訪問權(quán)限 (Access Control)

【Swift 3.1】24 -訪問權(quán)限 (Access Control)

自從蘋果2014年發(fā)布Swift,到現(xiàn)在已經(jīng)兩年多了,而Swift也來到了3.1版本翼悴。去年利用工作之余功炮,共花了兩個多月的時間把官方的Swift編程指南看完。現(xiàn)在整理一下筆記饱须,回顧一下以前的知識。有需要的同學(xué)可以去看官方文檔>>台谊。


訪問權(quán)限可以限制我們的部分代碼被其他文件或模塊訪問蓉媳。這可以隱藏有些代碼的實現(xiàn)細(xì)節(jié)。我們可以指定類型(如類锅铅、結(jié)構(gòu)和枚舉)的訪問權(quán)限酪呻,也可以指定屬性、方法盐须、初始化器和下標(biāo)的訪問權(quán)限玩荠。

Swift也提供了默認(rèn)的訪問權(quán)限來減少寫明訪問權(quán)限的需要。

模塊和源文件 (Modules and Source Files)

Swift的訪問控制模型是基于模塊和源文件的概念贼邓。

一個模塊是一個代碼分配單元阶冈,一個框架或應(yīng)用就是一個代碼單元,可以使用import關(guān)鍵字被其他模塊導(dǎo)入塑径。

在Swift中女坑,在Xcode的每個構(gòu)建版本(例如一個應(yīng)用程序包或者框架)都被認(rèn)為是一個分離的模塊。

一個源文件是一個模塊里面的代碼文件(實際上就是應(yīng)用或框架里的一個文件)统舀。雖然通常來說把每個獨立的類型寫在不同的源文件中匆骗,但是一個源文件可以包含多個類型、方法等等誉简。

注意:和訪問權(quán)限相關(guān)的屬性碉就、類型和方法等等都統(tǒng)稱為實體(entities)。

訪問級別 (Access Levels)

Swift提供了5個訪問級別闷串,這些訪問級別與源文件和模塊有關(guān):

  • openpublic修飾的實體可以被模塊內(nèi)的任何文件訪問铝噩,也可以被導(dǎo)入了這個模塊的另一個模塊的文件使用。當(dāng)我們定義框架的公開接口時窿克,通常使用open或者pubilc骏庸。至于這兩者的區(qū)別,下面會講到年叮。
  • internal修飾的實體可以被模塊內(nèi)的任何文件訪問具被,模塊之外的其他文件不能訪問。當(dāng)定義應(yīng)用或者模塊內(nèi)的結(jié)構(gòu)時只损,通常使用internal一姿。
  • fileprivate修飾的實體可以在這個文件內(nèi)被訪問七咧。當(dāng)實現(xiàn)細(xì)節(jié)只在文件內(nèi)使用時,通常使用fileprivate對其他文件隱藏實現(xiàn)細(xì)節(jié)叮叹。
  • private修飾的實體只能被實體所在的聲明內(nèi)部訪問艾栋。當(dāng)實現(xiàn)細(xì)節(jié)只在聲明內(nèi)部使用時,使用private蛉顽。

open訪問權(quán)限是最高的蝗砾,而private訪問權(quán)限是最低的。

open訪問權(quán)限只適用于class和class的成員携冤,與public的不同在于:

  • public修飾的class悼粮,或被其他更嚴(yán)格的訪問級別修飾,這個class只能在當(dāng)前模塊內(nèi)被繼承曾棕。
  • public修飾的class成員扣猫,或被其他更嚴(yán)格的訪問級別修飾,這些成員只能被當(dāng)前模塊內(nèi)的子類重寫翘地。
  • open修飾的class申尤,能在當(dāng)前模塊內(nèi)被繼承,也可以在導(dǎo)入了這個模塊的另一個模塊內(nèi)被繼承衙耕。
  • open修飾的class成員昧穿,能被當(dāng)前模塊內(nèi)的子類重寫,也可以被導(dǎo)入了這個模塊的另一個模塊內(nèi)的子類重寫臭杰。
訪問級別指導(dǎo)原則 (Guiding Principle of Access Levels)

Swift的訪問級別遵循一個總的原則:一個實體不能定義在訪問權(quán)限比它更低的實體里面粤咪。例如:

  • 一個public變量不能定義在internal谚中、fileprivate或者private修飾的類型中渴杆。
  • 一個方法的訪問權(quán)限不能高于他們參數(shù)和返回值類型
默認(rèn)訪問權(quán)限 (Default Access Levels)

如果我們沒有明確指定實體的權(quán)限,代碼中的所有實體都有一個默認(rèn)的權(quán)限internal宪塔。

單目標(biāo)應(yīng)用的訪問級別 (Access Levels for Single-Target Apps)

當(dāng)我們編寫一個單目標(biāo)應(yīng)用時磁奖,我們不必讓外部訪問應(yīng)用的模塊。默認(rèn)的internal權(quán)限都已經(jīng)滿足要求某筐。所以我們不必自定義訪問級別比搭。但是,有是有我們需要使用fileprivate或者private來隱藏一些功能實現(xiàn)細(xì)節(jié)南誊。

框架的訪問級別 (Access Levels for Frameworks)

當(dāng)我們在開發(fā)一個框架時身诺,使用open或者public來標(biāo)記那些想要公開的API,其他模塊導(dǎo)入這個框架就可以訪問公開的API抄囚。

單元測試目標(biāo)的訪問級別 (Access Levels for Unit Test Targets)

當(dāng)使用單元測試目標(biāo)編寫應(yīng)用時霉赡,為了測試,應(yīng)用的代碼需要向那個模塊公開幔托。默認(rèn)情況下穴亏,只有openpublic修飾的實體才可以被其他模塊訪問蜂挪。然而,如果我們在導(dǎo)入產(chǎn)品模塊時嗓化,使用@testable屬性標(biāo)記棠涮,單元測試目標(biāo)可以訪問internal修飾的實體。

訪問權(quán)限語法 (Access Control Syntax)

各個訪問級別的語法如下:

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() {}

下面兩個實體的訪問權(quán)限默認(rèn)是internal

class SomeInternalClass {}              // implicitly internal
let someInternalConstant = 0            // implicitly internal

自定義類型 (Custom Types)

一個類型的訪問權(quán)限會影響類型成員(如類型的屬性刺覆、方法严肪、初始化器和下標(biāo))的默認(rèn)權(quán)限。如果類型的訪問權(quán)限為private或者fileprivate隅津,那么他的成員的默認(rèn)權(quán)限是private或者fileprivate诬垂;如果類型的權(quán)限是internal或者public,那么他的成員的默認(rèn)權(quán)限是internal伦仍。

注意:一個public類型结窘,他的成員默認(rèn)是internal,如果想讓成員的權(quán)限也是public充蓝,我們必須明確寫出隧枫。

public class SomePublicClass {                  // class明確標(biāo)記為public
    public var somePublicProperty = 0            // class成員明確標(biāo)記為public
    var someInternalProperty = 0                 // class成員默認(rèn)是internal
    fileprivate func someFilePrivateMethod() {}  // class成員明確標(biāo)記為fileprivate
    private func somePrivateMethod() {}          // class成員明確標(biāo)記為private
}
 
class SomeInternalClass {                       // class默認(rèn)是internal
    var someInternalProperty = 0                 // class成員默認(rèn)是internal
    fileprivate func someFilePrivateMethod() {}  // class成員明確標(biāo)記為fileprivate
    private func somePrivateMethod() {}          // class成員明確標(biāo)記為private
}
 
fileprivate class SomeFilePrivateClass {        // class明確標(biāo)記為fileprivate
    func someFilePrivateMethod() {}              // class成員默認(rèn)是fileprivate
    private func somePrivateMethod() {}          // class成員明確標(biāo)記為private
}
 
private class SomePrivateClass {                // class明確標(biāo)記為private
    func somePrivateMethod() {}                  // class成員默認(rèn)是private
}
多元組類型 (Tuple Types)

多元組的訪問權(quán)限是以多元組內(nèi)元素訪問權(quán)限最低的為準(zhǔn)。例如谓苟,一個元素的權(quán)限是internal官脓,另一個是private,那么這個多元組的訪問權(quán)限是private涝焙。

方法類型 (Function Types)

方法的訪問權(quán)限是以參數(shù)類型和返回值類型的最低權(quán)限為準(zhǔn)卑笨。如果參數(shù)類型和返回值類型的最低權(quán)限不能滿足要求,需要我們明確寫出方法的權(quán)限仑撞。

下面是一個例子:

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // function implementation goes here
}

實際上這個例子是不能編譯通過的赤兴。方法的返回值是一個多元組,其中一個元素的權(quán)限為internal隧哮,另一個是private桶良,所以多元組的權(quán)限是private。因為返回值的權(quán)限是private沮翔,所以必須明確寫出方法的權(quán)限是private

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // function implementation goes here
}

someFunction()標(biāo)記為public或者internal陨帆,或者使用默認(rèn)的internal權(quán)限都是不行的。

枚舉類型 (Enumeration Types)

枚舉的每一個case的權(quán)限自動地與枚舉的權(quán)限相同采蚀,我們不能為單個case定義不同的權(quán)限疲牵。

下面的CompassPoint被標(biāo)記為public,那么每個case的權(quán)限也是public榆鼠。

public enum CompassPoint {
    case north
    case south
    case east
    case west
}
原始值和關(guān)聯(lián)類型 (Raw Values and Associated Values)

枚舉的原始值和關(guān)聯(lián)類型的訪問權(quán)限不能低于枚舉的訪問權(quán)限纲爸。例如,不能把private修飾的類型作為internal修飾的枚舉的關(guān)聯(lián)值類型璧眠。

嵌套類型 (Nested Types)

private類型里定義的嵌套類型缩焦,那么嵌套類型的權(quán)限也是private读虏;在fileprivate類型里定義的嵌套類型,那么嵌套類型的權(quán)限也是fileprivate袁滥;在public或者internal類型里定義的嵌套類型盖桥,那么嵌套類型的權(quán)限也是internal。如果想要嵌套類型是public的题翻,那么需要明確使用public修飾揩徊。

子類化 (Subclassing)

子類的權(quán)限不能高于父類的權(quán)限。

下面是一個例子嵌赠,B重寫了父類的someMethod()方法塑荒,并且權(quán)限是internal,高于這個方法在父類的權(quán)限fileprivate

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

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

子類成員可以調(diào)用父類的比子類成員訪問權(quán)限更低的成員姜挺,只要調(diào)用父類成員的位置滿足父類成員的權(quán)限要求(也就是說齿税,在同一個源文件內(nèi)調(diào)用父類的fileprivate成員,或者是在同一個模塊內(nèi)調(diào)用internal成員)炊豪。

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

因為父類A和子類B定義在同一個文件凌箕,所以在BsomeMethod()方法中調(diào)用super.someMethod()是有效的。

常量词渤、變量牵舱、屬性和下標(biāo) (Constants, Variables, Properties and Subscripts)

常量爽丹、變量或者屬性的權(quán)限不能高于他們的類型倦畅。

如果常量、變量庐冯、屬性或者下標(biāo)的類型是private高氮,那么常量慧妄、變量、屬性或下標(biāo)也必須用private修飾:

private var privateInstance = SomePrivateClass()
Getters and Setters

常量纫溃、變量腰涧、屬性和下標(biāo)的getter和setter方法的權(quán)限韧掩,默認(rèn)情況下與常量紊浩、變量、屬性和下標(biāo)的權(quán)限相同疗锐。

我們可以把setter的權(quán)限設(shè)置成低于對應(yīng)getter的權(quán)限坊谁。可以用fileprivate(set)滑臊、private(set)或者internal(set)來設(shè)置更低的權(quán)限口芍。

注意:這個規(guī)則適用于存儲屬性和計算屬性。雖然我們沒有明確寫出存儲屬性的getter和setter雇卷,但是Swift會默認(rèn)提供的鬓椭。

例如下面這個例子:

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

numberOfEdits屬性被private(set)修飾颠猴,setter的權(quán)限是private,所以numberOfEdits只能在TrackedString內(nèi)部被修改小染,對于TrackedString外部來說翘瓮,numberOfEdits是一個只讀屬性。而getter的權(quán)限默認(rèn)是TrackedString一樣裤翩,為internal资盅。

創(chuàng)建TrackedString實例,并修改value屬性:

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)")
// Prints "The number of edits is 3"

numberOfEdits能被外部訪問踊赠,但是不能被修改呵扛。

注意:我們可以分別設(shè)置getter和setter的權(quán)限。下面的例子是TrackedString的另外一個版本筐带,被public修飾今穿,所以這個結(jié)構(gòu)的成員默認(rèn)是internal的。我們可以結(jié)合publicprivate(set)使得umberOfEdits屬性的getter是public伦籍,而setterprivate荣赶。

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

初始化器 (Initializers)

自定義初始化器的權(quán)限可以低于或等于類型的權(quán)限,除了required初始化器鸽斟。required初始化器的權(quán)限只能等于類型的初始化器拔创。

對于方法和參數(shù),初始化器的參數(shù)權(quán)限不能低于初始化器的權(quán)限富蓄。

默認(rèn)初始化器 (Default Initializers)

默認(rèn)初始化器的權(quán)限與它要初始化的類型權(quán)限相同剩燥,除非這個類型的權(quán)限是public。對于public的類型立倍,他的默認(rèn)初始化器的權(quán)限是internal灭红。如果想讓其他模塊能使用public類型的無參數(shù)的初始化器,我們必須使用public標(biāo)記無參數(shù)的初始化器口注。

結(jié)構(gòu)類型的默認(rèn)逐一成員初始化器 (Default Memberwise Initializers for Structure Types)

如果結(jié)構(gòu)的任何存儲屬性都是private的变擒,那么默認(rèn)逐一成員初始化器也是private的;如果結(jié)構(gòu)的任何存儲屬性都是fileprivate的寝志,那么默認(rèn)逐一成員初始化器也是fileprivate的娇斑。否則默認(rèn)逐一成員初始化器是internal的。

如果想讓默認(rèn)逐一成員初始化器是public的材部,我們必須自己定義一個逐一成員初始化器毫缆,并用public修飾。

協(xié)議 (Protocols)

如果想用訪問級別修飾協(xié)議乐导,那么需要在定義協(xié)議的時候?qū)懮显L問級別苦丁。

協(xié)議里的每一個要求的權(quán)限默認(rèn)是與當(dāng)前協(xié)議權(quán)限相同,并且不能設(shè)置成與當(dāng)前協(xié)議不一樣的權(quán)限物臂。

注意:如果定義了一個public協(xié)議旺拉,那么協(xié)議里面的所有要求的權(quán)限也是public产上。這個規(guī)則不同于其他類型,如果其他類型定義為public蛾狗,那么它的成員默認(rèn)是internal的蒂秘。

協(xié)議繼承 (Protocol Inheritance)

如果定義了一個新的協(xié)議繼承于一個已經(jīng)存在的協(xié)議,新的協(xié)議權(quán)限不能高于已經(jīng)存在的協(xié)議淘太。不能定義一個public協(xié)議繼承于internal協(xié)議姻僧。

協(xié)議一致性 (Protocol Conformance)

一個類型可以遵循權(quán)限比它低的協(xié)議。例如蒲牧,可以定義一個public類型撇贺,然后遵循一個internal協(xié)議。

如果一個類型是public的冰抢,遵循于internal協(xié)議松嘶,那么這個協(xié)議在public類型的實現(xiàn)也是internal的。

擴(kuò)展 (Extensions)

在擴(kuò)展中新添加的類型成員的權(quán)限默認(rèn)與原類型定義的成員權(quán)限相同挎扰。如果擴(kuò)展了一個public或者internal的類型翠订,那么新添加的成員權(quán)限是internal;如果擴(kuò)展了一個fileprivate的類型遵倦,那么新添加的成員權(quán)限是fileprivate尽超;如果擴(kuò)展了一個private的類型,那么新添加的成員權(quán)限是private梧躺。

同樣地似谁,我們可以明確的指定擴(kuò)展的訪問權(quán)限(例如使用private extension),那么新指定的權(quán)限將會替代原類型的默認(rèn)權(quán)限掠哥。

使用擴(kuò)展遵循協(xié)議 (Adding Protocol Conformance with an Extension)

如果我們使用擴(kuò)展來遵循協(xié)議巩踏,那么我們不能明確指定擴(kuò)展的權(quán)限。擴(kuò)展對協(xié)議要求的實現(xiàn)的權(quán)限與協(xié)議的權(quán)限相同续搀。

泛型 (Generics)

泛型類型和泛型方法的訪問權(quán)限塞琼,是泛型類型或者泛型方法與類型約束的最低權(quán)限。

類型別名 (Type Aliases)

類型別名的權(quán)限可以小于或等于它原本的類型禁舷。例如彪杉,private的類型別名可以是privatefileprivate榛了、internal在讶、public或者open類型的別名煞抬。

注意:這個規(guī)則也適用于關(guān)聯(lián)值類型的類型別名霜大。


第二十四部分完。


如果有錯誤的地方革答,歡迎指正战坤!謝謝曙强!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市途茫,隨后出現(xiàn)的幾起案子碟嘴,更是在濱河造成了極大的恐慌,老刑警劉巖囊卜,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娜扇,死亡現(xiàn)場離奇詭異,居然都是意外死亡栅组,警方通過查閱死者的電腦和手機(jī)雀瓢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玉掸,“玉大人刃麸,你說我怎么就攤上這事∷纠耍” “怎么了泊业?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長啊易。 經(jīng)常有香客問我吁伺,道長,這世上最難降的妖魔是什么租谈? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任箱蝠,我火速辦了婚禮,結(jié)果婚禮上垦垂,老公的妹妹穿的比我還像新娘宦搬。我一直安慰自己,他們只是感情好劫拗,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布间校。 她就那樣靜靜地躺著,像睡著了一般页慷。 火紅的嫁衣襯著肌膚如雪憔足。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天酒繁,我揣著相機(jī)與錄音滓彰,去河邊找鬼。 笑死州袒,一個胖子當(dāng)著我的面吹牛揭绑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼他匪,長吁一口氣:“原來是場噩夢啊……” “哼菇存!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起邦蜜,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤依鸥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后悼沈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贱迟,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年絮供,在試婚紗的時候發(fā)現(xiàn)自己被綠了关筒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡杯缺,死狀恐怖蒸播,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情萍肆,我是刑警寧澤袍榆,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站塘揣,受9級特大地震影響包雀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜亲铡,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一才写、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奖蔓,春花似錦赞草、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疑务,卻和暖如春沾凄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背知允。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工撒蟀, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人温鸽。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓保屯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子配椭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容