Swift:訪問修飾符

訪問級(jí)別:

Swift 提供了五種不同的訪問級(jí)別,分別是::open、public 、internal、fileprivate、private,訪問權(quán)限依次由高到低。

open:可以被任何模塊的代碼訪問,包括override(重寫)和繼承。

open只能用在類、類成員上。

public: 可以被任何模塊的代碼訪問,模塊內(nèi)是可以被override(重寫)和繼承的,而在但其他模塊不可以override(重寫)和繼承。

internal:只允許在定義的模塊中訪問,不允許在其他模塊中使用。默認(rèn)訪問級(jí)別,可寫可不寫。

fileprivate:修飾的屬性或者方法只能在當(dāng)前文件中訪問,包括override(重寫)和繼承,當(dāng)前類的extension 中也可以。如果一個(gè)文件中含有多個(gè)類,這些類中也可以。

private:修飾的屬性或者方法只能在當(dāng)前類中訪問,當(dāng)前類的extension 中也可以訪問。如果當(dāng)前文件有多個(gè)類,這些類中不可以訪問。

使用原則:

不能用一個(gè)更低訪問級(jí)別的實(shí)體來定義當(dāng)前實(shí)體

比如,以internal、fileprivate或private修飾的類型,不能定義一個(gè)public變量。因?yàn)閜ublic變量使用的地方,這些類型均不可用:

比如,函數(shù)的訪問級(jí)別不能高于其他參數(shù)類型和返回值類型,因?yàn)檫@種函數(shù)的使用場景,其組成部分類型不可用:

默認(rèn)級(jí)別:

若無顯式聲明,代碼中所有實(shí)體的訪問級(jí)別默認(rèn)為 internal。

如果定義一個(gè) public 或 internal 類型,其成員的訪問級(jí)別默認(rèn)為 internal;

如果定義一個(gè) fileprivate 或 private 類型,其成員的訪問級(jí)別默認(rèn)為 fileprivate 或 private。

訪問控制:

自定義類型:

1、如果想給自定類型指明訪問級(jí)別,那就在定義時(shí)指明。只要訪問級(jí)別允許,新類型就可以被使用。

例如,你定義了一個(gè) file-private 的類,它就只能在定義文件中被當(dāng)作屬性類型、函數(shù)參數(shù)或返回類型使用。

2、類型的訪問控制級(jí)別也會(huì)影響它的成員的默認(rèn)訪問級(jí)別(它的屬性,方法,初始化方法,下標(biāo))。

如果你將類型定義為 private 或 file private 級(jí)別,那么它的成員的默認(rèn)訪問級(jí)別也會(huì)是 private 或file private。如果將類型定義為 internal 或 public級(jí)別(或直接使用默認(rèn)級(jí)別而不顯式指出),那么它的成員的默認(rèn)訪問級(jí)別會(huì)是 internal 。

注意: public 的類型默認(rèn)擁有 internal 級(jí)別的成員,而不是 public。如果想讓其中的一個(gè)類型成員是 public 的你必須按實(shí)示例代碼指明。這個(gè)要求確保類型的面向公眾的 API 是你選擇的,并且可以避免將類型的內(nèi)部工作細(xì)節(jié)公開成 API 的失誤。

///public
public class HFPublicClass {
    public var publicProperty = 0
    var internalProperty = 0
    fileprivate func filePrivateMethod() {}
    private func privateMethod() {}
}

///Internal
class HFInternalClass {
    var internalProperty = 0
    fileprivate func filePrivateMethod() {}
    private func privateMethod() {}
}

///fileprivate
fileprivate class HFFilePrivateClass {
    func filePrivateMethod() {}
    private func privateMethod() {}
}

///private
private class HFPrivateClass {
    func privateMethod() {}
}

元組類型:

元組類型的訪問級(jí)別是所有類型里最嚴(yán)格的。

例如,如果將兩個(gè)不同類型的元素組成一個(gè)元組,一個(gè)元素的訪問級(jí)別是 internal,另一個(gè)是 private,那么這個(gè)元組類型是 private 級(jí)別的。

即元組類型的訪問級(jí)別,與其所有組成類型中訪問級(jí)別最低者相同。

函數(shù)類型:

函數(shù)類型的訪問級(jí)別由函數(shù)成員類型和返回類型中的最嚴(yán)格訪問級(jí)別決定。

如果函數(shù)的計(jì)算訪問級(jí)別與上下文環(huán)境默認(rèn)級(jí)別不匹配,必須在函數(shù)定義時(shí)顯式指出。

例如:

這個(gè)函數(shù)的返回類型是一個(gè)由兩個(gè)在自定義類型里定義的類組成的元組。

其中一個(gè)類是 “internal” 級(jí)別的,另一個(gè)是 “private”。

因此,這個(gè)元組的訪問級(jí)別是“private”(元組成員的最嚴(yán)級(jí)別,或者說級(jí)別最低的)。

由于返回類型是 private 級(jí)別的,必須使用 private 修飾符使其合法:

private func someFunction() -> (HFInternalClass, HFPrivateClass) {
    
}

枚舉類型:

枚舉中的獨(dú)立成員自動(dòng)使用該枚舉類型的訪問級(jí)別。不能給獨(dú)立的成員指明一個(gè)不同的訪問級(jí)別。

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

在上面的例子中 CompassPoint 有一個(gè)指明的“public”級(jí)別。

里面的成員 north , south , east , 和 west因此是“public”。

枚舉定義中的原始值和關(guān)聯(lián)值使用的類型必須有一個(gè)不低于枚舉的訪問級(jí)別。

例如,不能使用一個(gè) private 類型作為一個(gè) internal 級(jí)別的枚舉類型中的原始值類型。

struct HFSucess {
    
}
private struct HFFailure {
    
}
enum HFResult {
    case sucess(HFSucess)
    case failure(HFFailure)
}

嵌套類型:

private 級(jí)別的類型中定義的嵌套類型自動(dòng)為 private 級(jí)別。

fileprivate 級(jí)別的類型中定義的嵌套類型自動(dòng)為 fileprivate 級(jí)別。

public 或 internal 級(jí)別的類型中定義的嵌套類型自動(dòng)為 internal 級(jí)別。

如果想讓嵌套類型是 public 級(jí)別的,你必須將其顯式指明為 public。

子類的訪問級(jí)別不能高于父類。

重寫可以讓一個(gè)繼承類成員比它的父類中的更容易訪問。例如:

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

public 級(jí)別的類 A 有一個(gè) fileprivate 級(jí)別的 someMethod() 函數(shù)。

B 是 A 的子類,有一個(gè)降低的“internal”級(jí)別。

但是,類 B 對(duì) someMethod() 函數(shù)進(jìn)行了重寫即改為“internal”級(jí)別,這比 someMethod() 的原本實(shí)現(xiàn)級(jí)別更高。

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

子類成員調(diào)用父類中比子類更低訪問級(jí)別的成員,只要這個(gè)調(diào)用發(fā)生在一個(gè)允許的訪問級(jí)別上下文中(即對(duì) fileprivate 成員的調(diào)用要求父類在同一個(gè)源文件中,對(duì) internal 成員的調(diào)用要求父類在同一個(gè)模塊中)

因?yàn)楦割?A 和子類 B 定義在同一個(gè)源文件中,那么 B 類可以在 someMethod() 中調(diào)用父類的 someMethod() 。

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

常量、變量、屬性不能擁有比它們類型更高的訪問級(jí)別。

例如,不能寫一個(gè)public 的屬性而它的類型是 private 的。

類似的,下標(biāo)也不能擁有比它的索引類型和返回類型更高的訪問級(jí)別。

Getters 和 Setters

常量、變量、屬性和下標(biāo)的 getter 和 setter 自動(dòng)接收它們所屬常量、變量、屬性和下標(biāo)的訪問級(jí)別。

可以給 setter 函數(shù)一個(gè)比相對(duì)應(yīng) getter 函數(shù)更低的訪問級(jí)別以限制變量、屬性、下標(biāo)的讀寫權(quán)限。

可以通過在 var 和 subscript 的置入器之前書寫 fileprivate(set) , private(set) , 或 internal(set) 來聲明更低的訪問級(jí)別。

注意:這個(gè)規(guī)則應(yīng)用于存儲(chǔ)屬性和計(jì)算屬性。

即使沒有給一個(gè)存儲(chǔ)屬性書寫一個(gè)明確的 getter 和 setter,Swift 會(huì)為你合成一個(gè) getter 和 setter 以訪問到存儲(chǔ)屬性的隱式存儲(chǔ)。

使用 fileprivate(set) , private(set) 和 internal(set) 可以改變這個(gè)合成的 setter 的訪問級(jí)別,同樣也可以改變計(jì)算屬性的訪問級(jí)別。

下面的例子定義了一個(gè)稱為 TrackedString 的結(jié)構(gòu)體,它保持追蹤一個(gè)字符串屬性的修改次數(shù):

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

TrackedString 結(jié)構(gòu)體定義了一個(gè)可存儲(chǔ)字符串的屬性 value ,它又一個(gè)初始值 "" (空字符串)。

這個(gè)結(jié)構(gòu)圖同樣定義了一個(gè)可存儲(chǔ)整數(shù)的屬性 numberOfEdits ,它被用于記錄 value 的修改次數(shù)。

這個(gè)記錄由 value 屬性中的didset屬性實(shí)現(xiàn),它會(huì)增加 numberOfEdits 的值一旦 value 被設(shè)為一個(gè)新值。

TrackedString 結(jié)構(gòu)體和 value 屬性都沒有顯式指出訪問級(jí)別修飾符,因此它們都遵循默認(rèn)的 internal 級(jí)別。

numberOfEdits 屬性的訪問級(jí)別已經(jīng)標(biāo)注為 private(set) 以說明這個(gè)屬性的 getter 是默認(rèn)的 internal 級(jí)別,但是這個(gè)屬性只能被 TrackedString 內(nèi)的代碼設(shè)置。這允許 TrackedString 在內(nèi)部修改 numberOfEdits 屬性,而且可以展示這個(gè)屬性作為一個(gè)只讀屬性當(dāng)在結(jié)構(gòu)體定義之外使用時(shí)——包括 TrackedString 的擴(kuò)展。

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"

創(chuàng)建了一個(gè) TrackedString 的實(shí)例并修改了幾次字符串的值,可以看到 numberOfEdits 屬性的值更新到匹配修改的次數(shù)。

盡管可以從別的源文件中詢問到 numberOfEdits 屬性的當(dāng)前值,但不能從別的源文件中修改該屬性的值。
這個(gè)限制保護(hù)了 TrackedString 編輯追蹤功能的實(shí)現(xiàn)細(xì)節(jié),并同時(shí)為該功能的一個(gè)方面提供方便的訪問。

若有必要也可以顯式指明 getter 和 setter方法。

下面的例子提供了一個(gè)定義為 public 級(jí)別的 TrackedString 結(jié)構(gòu)體。

結(jié)構(gòu)體成員(包括 numberOfEdits 屬性)因此有一個(gè)默認(rèn)的 internal 級(jí)別。

可以設(shè)置 numberOfEdits 屬性的getter方法為 public,setter 方法為 private 級(jí)別,通過結(jié)合 public 和 private(set) 訪問級(jí)別修飾符:

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

初始化器:

我們可以給自定義初始化方法設(shè)置一個(gè)低于或等于它的所屬的類的訪問級(jí)別。

唯一的例外是必要初始化器(定義在必要初始化器),必要初始化器必須和它所屬類的訪問級(jí)別一致。

就像函數(shù)和方法的參數(shù)一樣,初始化器的參數(shù)類型不能比初始化方法的訪問級(jí)別還低。

默認(rèn)初始化器

正如默認(rèn)初始化器中描述的那樣,Swift 自動(dòng)為任何結(jié)構(gòu)體和類提供一個(gè)無參數(shù)的默認(rèn)初始化方法,以給它的屬性提供默認(rèn)值但不會(huì)提供給初始化器自身。

默認(rèn)初始化方法與所屬類的訪問級(jí)別一致,除非該類型定義為 public 。

如果一個(gè)類定義為 public ,那么默認(rèn)初始化方法為 internal 級(jí)別。如果想一個(gè) public 類可以被一個(gè)無參初始化器初始化當(dāng)在另一個(gè)模塊中使用時(shí),必須顯式提供一個(gè) public 的無參初始化方法。

結(jié)構(gòu)體的默認(rèn)成員初始化器

如果結(jié)構(gòu)體的存儲(chǔ)屬性時(shí) private 的,那么它的默認(rèn)成員初始化方法就是 private 級(jí)別。

如果結(jié)構(gòu)體的存儲(chǔ)屬性時(shí) file private 的,那么它的默認(rèn)成員初始化方法就是 file private 級(jí)別。

否則就是默認(rèn)的 internal 級(jí)別。

正如以上默認(rèn)初始化的描述,如果想在另一個(gè)模塊中使用結(jié)構(gòu)體的成員初始化方法,必須提供在定義中提供一個(gè) public 的成員初始化方法。

協(xié)議:

如果想給一個(gè)協(xié)議類型分配一個(gè)顯式的訪問級(jí)別,那就在定義時(shí)指明,這讓創(chuàng)建的協(xié)議可以在一個(gè)明確的訪問上下文中被接受。

協(xié)議定義中的每一個(gè)要求的訪問級(jí)別都自動(dòng)設(shè)為與該協(xié)議相同。不能將一個(gè)協(xié)議要求的訪問級(jí)別設(shè)為與協(xié)議不同。這保證協(xié)議的所有要求都能被接受該協(xié)議的類型所見。

注意如果定義了一個(gè) public 的協(xié)議,該協(xié)議的規(guī)定要求在被實(shí)現(xiàn)時(shí)擁有一個(gè) public 的訪問級(jí)別。這個(gè)行為不同于其他類型,一個(gè) public 的類型的成員時(shí) internal 訪問級(jí)別。

協(xié)議繼承

如果定義了一個(gè)繼承已有協(xié)議的協(xié)議,這個(gè)新協(xié)議最高與它繼承的協(xié)議訪問級(jí)別一致。

例如不能寫一個(gè) public 的協(xié)議繼承一個(gè) internal 的協(xié)議。

協(xié)議遵循

類型可以遵循更低訪問級(jí)別的協(xié)議。

例如,可以定義一個(gè)可在其他模塊使用的 public 類型,但它就只能在定義模塊中使用如果遵循一個(gè) internal 的協(xié)議。

遵循了協(xié)議的類的訪問級(jí)別取這個(gè)協(xié)議和該類的訪問級(jí)別的最小者。如果這個(gè)類型是 public 級(jí)別的,它所遵循的協(xié)議是 internal 級(jí)別,這個(gè)類型就是 internal 級(jí)別的。

當(dāng)寫或是擴(kuò)張一個(gè)類型以遵循協(xié)議時(shí),必須確保該類按協(xié)議要求的實(shí)現(xiàn)方法與該協(xié)議的訪問級(jí)別一致。

例如,一個(gè) public 的類遵循一個(gè) internal 協(xié)議,該類的方法實(shí)現(xiàn)至少是 “internal” 的。

注意:在 Swift 和 Objective-C 中協(xié)議遵循是全局的——一個(gè)類不可能在一個(gè)程序中用不同方法遵循一個(gè)協(xié)議。

擴(kuò)展:

可以在任何可訪問的上下文環(huán)境中對(duì)類、結(jié)構(gòu)體、或枚舉進(jìn)行擴(kuò)展。

在擴(kuò)展中添加的任何類型成員都有著被擴(kuò)展類型相同的訪問權(quán)限。

如果擴(kuò)展一個(gè)公開或者內(nèi)部類型,添加的任何新類型成員都擁有默認(rèn)的內(nèi)部訪問權(quán)限。

如果擴(kuò)展一個(gè)文件內(nèi)私有的類型,添加的任何新類型成員都擁有默認(rèn)的私有訪問權(quán)限。

如果擴(kuò)展一個(gè)私有類型,添加的任何新類型成員都擁有默認(rèn)的私有訪問權(quán)限。

或者,可以顯式標(biāo)注擴(kuò)展的訪問級(jí)別(例如, private extension )已給擴(kuò)展中的成員設(shè)置新的默認(rèn)訪問級(jí)別。這個(gè)默認(rèn)同樣可以在擴(kuò)展中為單個(gè)類型成員重寫。

不能給用于協(xié)議遵循的擴(kuò)展顯式標(biāo)注訪問權(quán)限修飾符。

相反,在擴(kuò)展中使用協(xié)議自身的訪問權(quán)限作為協(xié)議實(shí)現(xiàn)的默認(rèn)訪問權(quán)限。

擴(kuò)展中的私有成員

在同一文件中的擴(kuò)展比如類、結(jié)構(gòu)體或者枚舉,可以寫成類似多個(gè)部分的類型聲明。

可以:

在原本的聲明中聲明一個(gè)私有成員,然后在同一文件的擴(kuò)展中訪問它;
在擴(kuò)展中聲明一個(gè)私有成員,然后在同一文件的其他擴(kuò)展中訪問它;
在擴(kuò)展中聲明一個(gè)私有成員,然后在同一文件的原本聲明中訪問它。

泛型:

泛指類型和泛指函數(shù)的訪問級(jí)別取泛指類型或函數(shù)以及泛型類型參數(shù)的訪問級(jí)別的最小值。

類型別名:

為實(shí)現(xiàn)訪問控制,定義的任何類型別名,都被視為與原類型不同的類型。類型別名的訪問級(jí)別不高于原類型。規(guī)則同樣適用于用于滿足協(xié)議遵守的關(guān)聯(lián)類型的類型別名。

typealias HFMaterial = Dictionary<String, Any>

class HFObject {
    private(set) var info: HFMaterial
    init(_ info: HFMaterial) {
        self.info = info
    }
}

public typealias HFPhone = HFObject
private typealias HFWine = HFObject

protocol HFFactory {
    associatedtype Product
    func produce(with material: HFMaterial) -> Product
}

struct HFWinery: HFFactory {
    typealias Product = HFWine
    func produce(with material: HFMaterial) -> HFWine {
        return HFWine(material)
    }
}

如圖可知:

HFPhone 類型別名的訪問級(jí)別 public 高于 HFObject 類型的 internal,報(bào)錯(cuò);
HFWine 類型別名的訪問級(jí)別 private 低于 HFObject 類型的 internal,正常;
HFWinery 遵守 HFFactory 協(xié)議,關(guān)聯(lián)類型別名 Product 的訪問級(jí)別 internal 高于 HFWine 類型別名的 private,報(bào)錯(cuò)。

參考:
Swift小知識(shí)點(diǎn)之 open,public,internal,fileprivate,private訪問修飾符

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容