標籤:
本章節由CocoaChina翻譯小組成員 JaceFu (個人部落格)翻譯自蘋果的官方文檔:The Swift Programming Language--Language Guide--Access Control。校對:ChildhoodAndy( 部落格 ),敬請勘誤。 蘋果在發布了Xcode 6 Bate 4後為Swift添加了新的特性--存取控制(Access Control),並且更新了The Swift Programming Language文檔,我抽空把這篇文檔翻譯了一下,下面讓我們來詳細瞭解一下Access Control。
本頁內容包括:
1.模組和源檔案
2.存取層級
(1)存取層級的使用原則
(2)預設存取層級
(3)單目標應用程式的存取層級
(4)Framework的存取層級
3.存取控制文法
4.自訂類型
(1)元群組類型
(2)函數類型
(3)枚舉類型
(4)原始值和關聯值
(5)巢狀型別
5.子類
6.常量、變數、屬性、下標
(1)Getter和Setter
7.初始化
(1)預設初始化方法
(2)結構體的預設成員初始化方法
8.協議
(1)協議繼承
(2)協議一致性
9.擴充
(1)協議的擴充
10.泛型
11.類型別名 存取控制可以限定你在源檔案或模組中存取碼的層級,也就是說可以控制哪些代碼你可以訪問,哪些代碼你不能訪問。這個特性可以讓我們隱藏功能實現的一些細節,並且可以明確的指定我們提供給其他人的介面中哪些部分是他們可以使用的,哪些是他們看不到的。 你可以明確的給類、結構體、枚舉、設定存取層級,也可以給屬性、函數、初始化方法、基本類型、下標索引等設定存取層級。協議也可以被限定在一定的範圍內使用,包括協議裡的全域常量、變數和函數。 在提供了不同存取層級的同時,Swift 並沒有規定我們要在任何時候都要在代碼中明確指定存取層級。其實,如果我們作為獨立開發人員在開發我們自己的 app,而不是在開發一些 Framework 的時候,我們完全可以不用明確的指定代碼的存取層級。 注意:為方便起見,在代碼中可以設定存取層級的它們(屬性、基本類型、函數等)在下面的章節中我們稱之為“實體”。
模組和源檔案Swift 中的存取控制模型基於模組和源檔案這兩個概念。 模組指的是 Framework 或 App bundle 。在 Swift 中,可以用 import 關鍵字引入自己的工程。 在 Swift 中,Framework 或 App bundle 被作為模組處理。如果你是為了實現某個通用的功能,或者是為了封裝一些常用方法而將代碼打包成 Framework,這個 Framework 在 Swift 中就被稱為模組。不論它被引入到某個 App 工程或者其他的Framework,它裡面的一切(屬性、函數等)都屬於這個模組。 源檔案指的是 Swift 中的Swift File,就是編寫 Swift 代碼的檔案,它通常屬於一個模組。通常一個源檔案包含一個類,在類中又包含函數、屬性等類型。
存取層級Swift 提供了三種不同的存取層級。這些存取層級相對於源檔案中定義的實體,同時也相對於這些源檔案所屬的模組。
1.Public:可以訪問自己模組或應用中源檔案裡的任何實體,別人也可以訪問引入該模組中源檔案裡的所有實體。通常情況下,某個介面或 Framework 是可以被任何人使用時,你可以將其設定為 public 層級。
2.Internal:可以訪問自己模組或應用中源檔案裡的任何實體,但是別人不能訪問該模組中源檔案裡的實體。通常情況下,某個介面或 Framework 作為內部結構使用時,你可以將其設定為 internal 層級。
3.Private:只能在當前源檔案中使用的實體,稱為私人實體。使用 private 層級,可以用作隱藏某些功能的實現細節。 Public 為最進階存取層級,Private 為最低級存取層級。
存取層級的使用原則在 Swift 中,存取層級有如下使用原則:存取層級統一性。 比如說: 1.一個 public 存取層級的變數,不能將它的類型定義為 internal 和 private 的類型。因為變數可以被任何人訪問,但是定義它的類型不可以,所以這樣就會出現錯誤。2.函數的存取層級不能高於它的參數、傳回型別的存取層級。因為如果函數定義為 public 而參數或者傳回型別定義為 internal 或 private,就會出現函數可以被任何人訪問,但是它的參數和傳回型別不可以,同樣會出現錯誤。
預設存取層級代碼中的所有實體,如果你不明確的定義其存取層級,那麼它們預設為 internal 層級。在大多數情況下,我們不需要明確的設定實體的存取層級,因為我們大多數時候都是在開發一個 App bundle。
單目標應用程式的存取層級當你編寫一個單目標應用程式時,該應用的所有功能都是為該應用服務,不需要提供給其他應用或者模組使用,所以我們不需要明確設定存取層級,使用預設的存取層級 internal 即可。但是如果你願意,你也可以使用 private 層級,用於隱藏一些功能的實現細節。
Framework的存取層級當你開發 Framework 時,就需要把一些實體定義為 public 層級,以便其他人匯入該 Framework 後可以正常使用其功能。這些被你定義為 public 的實體,就是這個 Framework 的API。
注意:Framework 的內部實現細節依然可以使用預設的 internal 層級,或者也可以定義為 private 層級。只有你想將它作為 API 的實體,才將其定義為 public 層級。
存取控制文法 通過修飾符 public、internal、private 來聲明實體的存取層級:
- public class SomePublicClass {}
- internal class SomeInternalClass {}
- private class SomePrivateClass {}
-
- public var somePublicVariable = 0
- internal let someInternalConstant = 0
- private func somePrivateFunction() {}
除非有特殊的說明,否則實體都使用預設的存取層級 internal,可以查閱 Default Access Levels 這一節。這意味著 SomeInternalClass 和 someInternalConstant 不用明確的使用修飾符聲明存取層級,但是他們任然擁有隱式的存取層級 internal:
- class SomeInternalClass {} // 隱式存取層級 internal
- var someInternalConstant = 0 // 隱式存取層級 internal
自訂類型如果你想為一個自訂類型指定一個明確的存取層級,那麼你要明確一點。那就是你要確保新類型的存取層級和它實際的範圍相匹配。比如說,如果某個類裡的屬性、函數、傳回值它們的範圍僅在當前的源檔案中,那麼你就可以將這個類聲明為 private 類,而不需要聲明為 public 或者 internal 類。 類的存取層級也可以影響到類成員(屬性、函數、初始化方法等)的預設存取層級。如果你將類聲明為 private 類,那麼該類的所有成員的預設存取層級也會成為 private 。如果你將類聲明為 public 或者 internal 類(或者不明確的指定存取層級,而使用預設的 internal 存取層級),那麼該類的所有成員的存取層級是 internal 。
注意:上面提到,一個 public 類的所有成員的存取層級預設為 internal 層級,而不是 public 層級。如果你想將某個成員聲明為 public 層級,那麼你必須使用修飾符明確的聲明該成員。這樣做的好處是,在你定義公用介面API的時候,可以明確的選擇哪些屬性或方法是需要公開的,哪些是內部使用的,可以避免將內部使用的屬性方法公開成公用API的錯誤。
- public class SomePublicClass { // 顯示的 public 類
- public var somePublicProperty = 0 // 顯示的 public 類成員
- var someInternalProperty = 0 // 隱式的 internal 類成員
- private func somePrivateMethod() {} // 顯示的 private 類成員
- }
-
- class SomeInternalClass { // 隱式的 internal 類
- var someInternalProperty = 0 // 隱式的 internal 類成員
- private func somePrivateMethod() {} // 顯示的 private 類成員
- }
-
- private class SomePrivateClass { // 顯示的 private 類
- var somePrivateProperty = 0 // 隱式的 private 類成員
- func somePrivateMethod() {} // 隱式的 private 類成員
- }
元群組類型元組的存取層級使用是所有類型的存取層級使用中最為嚴謹的。比如說,如果你構建一個包含兩種不同類型元素的元組,其中一個元素類型的存取層級為 internal,另一個為 private 層級,那麼這個元組的存取層級為 private 。也就是說元組的存取層級遵循它裡面元組中最低級的存取層級。
注意:元組不同於類、結構體、枚舉、函數那樣有單獨的定義。元組的存取層級是在它被使用時自動推匯出的,而不是明確的聲明。
函數類型函數的存取層級需要根據該函數的參數類型存取層級、傳回型別存取層級得出。如果根據參數類型和傳回型別得出的函數存取層級不符合上下文,那麼就需要明確的聲明該函數的存取層級。 下面的例子中定義了一個全域函數名為 someFunction ,並且沒有明確的聲明其存取層級。你也許會認為該函數應該擁有預設的存取層級 internal,但事實並非如此。事實上,如果按下面這種寫法,編譯器是無法編譯通過的:
- func someFunction() -> (SomeInternalClass, SomePrivateClass) {
- // function implementation goes here
- }
我們可以看到,這個函數的傳回型別是一個元組,該元組中包含兩個自訂的類(可查閱Custom Types)。其中一個類的存取層級是 internal,另一個的存取層級是 private,所以根據元組存取層級的原則,該元組的存取層級是 private(元組的存取層級遵循它裡面元組中最低級的存取層級)。 因為該函數傳回型別的存取層級是private,所以你必須使用private修飾符,明確地申請該函數:
- private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
- // function implementation goes here
- }
將該函式宣告為public或internal,或者使用預設的存取層級internal都是錯誤的,因為如果把該函數當做 public 或 internal 層級來使用的話,是無法得到private層級的傳回值的。
枚舉類型枚舉中成員的存取層級繼承自該枚舉,你不能為枚舉中的成員指定存取層級。 比如下面的例子,枚舉 CompassPoint 被明確的聲明為 public 層級,那麼它的成員 North,South,East,West 的存取層級同樣也是 public:
- public enum CompassPoint {
- case North
- case South
- case East
- case West
- }
原始值和關聯值用於枚舉定義中的任何原始值,或關聯的實值型別必須有一個存取層級,至少要高於枚舉的存取層級。比如說,你不能在一個 internal 存取層級的枚舉中定義 private 層級的原始實值型別。
巢狀型別如果在private層級的類型中定義巢狀型別,那麼該巢狀型別就自動擁有 private 存取層級。如果在 public 或者 internal 層級的類型中定義巢狀型別,那麼該巢狀型別自動擁有 internal 存取層級。如果想讓巢狀型別擁有 public 存取層級,那麼需要對該巢狀型別進行明確的存取層級聲明。
子類子類的存取層級不得高於父類的存取層級。比如說,父類的存取層級是 internal ,子類的存取層級就不能聲明為 public 。 此外,在滿足子類不高於父類存取層級以及遵循各存取層級範圍(即模組或源檔案)的前提下,你可以重寫任意類成員(方法、屬性、初始化方法、下標索引等)。 如果我們無法直接存取某個類中的屬性或函數等,那麼可以繼承該類,從而可以更容易的訪問到該類的類成員。下面的例子中,類A的存取層級是 public ,它包含一個函數 someMethod,存取層級為private。類B繼承類A,並且存取層級聲明為 internal ,但是在類B中重寫了類A中存取層級為 private 的方法 someMethod,並重新聲明為 internal 層級。通過這種方式,我們就可以訪問到某類中 private 層級的類成員,並且可以重新聲明其存取層級,以便其他人使用:
- public class A {
- private func someMethod() {}
- }
-
- internal class B: A {
- override internal func someMethod() {}
- }
只要滿足子類不高於父類存取層級以及遵循各存取層級範圍的前提下(即 private 的範圍在同一個源檔案中,internal 的範圍在同一個模組下),我們甚至可以在子類中,用子類成員訪問父類成員,哪怕父類成員的存取層級比子類成員的要低:
- public class A {
- private func someMethod() {}
- }
-
- internal class B: A {
- override internal func someMethod() {
- super.someMethod()
- }
- }
因為父類A和子類B定義在同一個源檔案中,所以在類B中可以在重寫的 someMethod 方法中調用 super.someMethod() 。
常量、變數、屬性、下標 常量、變數、屬性不能擁有比它們的類型更高的存取層級。比如說,你定義一個 public 層級的屬性,但是它的類型是 private 層級的,這是編譯器不允許的。同樣,下標也不能擁有比索引類型或傳回型別更高的存取層級。 如果常量、變數、屬性、下標索引的定義類型是 private 層級的,那麼它們必須要明確的聲明存取層級為 private:
- private var privateInstance = SomePrivateClass()
Getter和Setter 常量、變數、屬性、下標索引的 Getters 和 Setters 的存取層級繼承自它們所屬成員的存取層級。 Setter 的存取層級可以低於對應的 Getter 的存取層級,這樣就可以控制變數、屬性或下標索引的讀寫權限。在var或subscript定義範圍之前,你可以通過private(set)或internal(set)先為它門的寫入權限聲明一個較低的存取層級。
注意:這個規定適用於用作儲存的屬性或用作計算的屬性。即使你不明確的聲明儲存屬性的 Getter 和 Setter,Swift 也會隱式的為其建立 Getter 和 Setter,用於對該屬性進行讀取操作。使用 private(set) 和internal(set) 可以改變 Swift 隱式建立的 Setter 的存取層級。在計算屬性中也是同樣的。 下面的例子中定義了一個結構體名為 TrackedString,它記錄了value屬性被修改的次數:
- struct TrackedString {
- private(set) var numberOfEdits = 0
- var value: String = "" {
- didSet {
- numberOfEdits++
- }
- }
- }
TrackedString 結構體定義了一個用於儲存的屬性名稱為 value,類型為 String,並將初始化值設為""(即一個Null 字元串)。該結構體同時也定義了另一個用於儲存的屬性名稱為 numberOfEdits ,類型為 Int,它用於記錄屬性 value 被修改的次數。這個功能的實現通過屬性 value 的 didSet 方法實現,每當給value賦新值時就會調用 didSet 方法,給 numberOfEdits 加一。 結構體 TrackedString 和它的屬性 value 均沒有明確的聲明存取層級,所以它們都擁有預設的存取層級 internal。但是該結構體的 numberOfEdits 屬性使用 private(set) 修飾符進行聲明,這意味著numberOfEdits 屬性只能在定義該結構體的源檔案中賦值。numberOfEdits 屬性的 Getter 依然是預設的存取層級 internal,但是 Setter 的存取層級是 private ,這表示該屬性只有在當前的源檔案中是可讀可寫的,在當前源檔案所屬的模組中它只是一個可讀的屬性。 如果你執行個體化 TrackedString 結構體,並且多次對value屬性的值進行修改,你就會看到 numberOfEdits 的值會隨著修改次數更改:
- var stringToEdit = TrackedString()
- stringToEdit.value = "This string will be tracked."
- stringToEdit.value += " This edit will increment numberOfEdits."
- stringToEdit.value += " So will this one."
- println("The number of edits is \(stringToEdit.numberOfEdits)")
- // prints "The number of edits is 3"
雖然你可以在其他的源檔案中執行個體化該結構體並且擷取到numberOfEdits屬性的值,但是你不能對其進行賦值。這樣就能很好的告訴使用者,你只管使用,而不需要知道其實現細節。
初始化我們可以給自訂的初始化方法指定存取層級,但是必須要低於或等於它所屬類的存取層級。但如果該初始化方法是必須要使用的話,那它的存取層級就必須和所屬類的存取層級相同(參見Required Initializers)。 如同函數或方法參數,初始化方法參數的存取層級也不能低於初始化方法的存取層級。
預設初始化方法Swift 為結構體、類都提供了一個預設的無參初始化方法,用於給它們的所有屬性提供賦值操作,但不會給出具體值。預設初始化方法可以參閱 Default Initializers 。預設初始化方法的存取層級與所屬類型的存取層級相同。
注意:如果一個類型被聲明為public層級,那麼預設的初始化方法的存取層級為 internal 。如果你想讓無參的初始化方法在其他模組中可以被使用,那麼你必須提供一個具有 public 存取層級的無參初始化方法。
結構體的預設成員初始化方法如果結構體中的任一儲存屬性的存取層級為 private ,那麼它的預設成員初始化方法存取層級就是 private 。儘管如此,結構體的初始化方法的存取層級依然是 internal 。 如果你想在其他模組中使用該結構體的預設成員初始化方法,那麼你需要提供一個存取層級為 public 的預設成員初始化方法。
協議如果你想為一個協議明確的聲明存取層級,那麼有一點需要注意,就是你要確保該協議只在你聲明的存取層級範圍中使用。 協議中的每一個必須要實現的函數都具有和該協議相同的存取層級。這樣才能確保該協議的使用者可以實現它所提供的函數。
注意:如果你定義了一個 public 存取層級的協議,那麼實現該協議提供的必要函數也會是public的存取層級。這一點不同於其他類型,比如 public 存取層級的其他類型,他們成員的存取層級為 internal 。
協議繼承如果定義了一個新的協議,並且該協議繼承了一個已知的協議,那麼新協議擁有的存取層級最高也只和被繼承協議的存取層級相同。比如說,你不能定義一個public的協議而去繼承一個internal的協議。
協議一致性類可以採用比自身存取層級低的協議。比如說,你可以定義一個 public 層級的類,可以讓它在其他模組中使用,同時它也可以採用一個 internal 層級的協議,並且只能在定義了該協議的模組中使用。 採用了協議的類的存取層級遵循它本身和採用協議中最低的存取層級。也就是說如果一個類是 public 層級,採用的協議是 internal 層級,那個採用了這個協議後,該類的存取層級也是 internal 。 如果你採用了協議,那麼實現了協議必須的方法後,該方法的存取層級遵循協議的存取層級。比如說,一個 public 層級的類,採用了 internal 層級的協議,那麼該類實現協議的方法至少也得是 internal 。
注意:在 Swift 中和 Objective-C 中一樣,協議的一致性保證了一個類不可能在同一個程式中用不同的方法採用同一個協議。
擴充你可以在條件允許的情況下對類、結構體、枚舉進行擴充。擴充成員應該具有和原始類成員一致的存取層級。比如你擴充了一個公用類型,那麼你新加的成員應該具有和原始成員一樣的預設的 internal 存取層級。 或者,你可以明確聲明擴充的存取層級(比如使用 private extension)給該擴充內所有成員指定一個新的預設存取層級。這個新的預設存取層級仍然可以被單獨成員所指定的存取層級所覆蓋。
協議的擴充如果一個擴充採用了某個協議,那麼你就不能對該擴充使用存取層級修飾符來聲明了。該擴充中實現協議的方法都會遵循該協議的存取層級。
泛型泛型型別或泛型函數的存取層級遵循泛型型別、函數本身、泛型型別參數三者中存取層級最低的層級。
類型別名任何被你定義的類型別名都會被視作為不同的類型,這些類型用於存取控制。一個類型別名的存取層級可以低於或等於這個類型的存取層級。比如說,一個private層級的類型別名可以設定給一個public、internal、private的類型,但是一個public層級的類型別名只能設定給一個public層級的類型,不能設定給internal或private的類類型。
注意:這條規則也適用於為滿足協議一致性而給相互關聯類型命名別名。
[轉]Swift存取控制