3,publicapp
訪問控制能夠限定你在源文件或模塊中訪問代碼的級別,也就是說能夠控制哪些代碼你能夠訪問,哪些代碼你不能訪問。這個特性可讓咱們隱藏功能實現的一些細節,而且能夠明確的指定咱們提供給其餘人的接口中哪些部分是他們可使用的,哪些是他們看不到的。框架
你能夠明確的給類、結構體、枚舉、設置訪問級別,也能夠給屬性、函數、初始化方法、基本類型、下標索引等設置訪問級別。協議也能夠被限定在必定的範圍內使用,包括協議裏的全局常量、變量和函數。ide
在提供了不一樣訪問級別的同時,Swift並無規定咱們要在任什麼時候候都要在代碼中明確指定訪問級別。其實,若是咱們做爲獨立開發者在開發咱們本身的app,而不是在開發一些Framework
的時候,咱們徹底能夠不用明確的指定代碼的訪問級別。函數
注意:爲方便起見,在代碼中能夠設置訪問級別的它們(屬性、基本類型、函數等)在下面的章節中咱們稱之爲「實體」。this
Swift中的訪問控制模型基於模塊和源文件這兩個概念。spa
模塊指的是Framework
或App bundle
。在Swift中,能夠用import
關鍵字引入本身的工程。code
在Swift中,Framewordk
或App bundle
被做爲模塊處理。若是你是爲了實現某個通用的功能,或者是爲了封裝一些經常使用方法而將代碼打包成Framework
,這個Framework
在Swift中就被稱爲模塊。不論它被引入到某個App工程或者其餘的Framework
,它裏面的一切(屬性、函數等)都屬於這個模塊。繼承
源文件指的是Swift中的Swift File
,就是編寫Swift代碼的文件,它一般屬於一個模塊。一般一個源文件包含一個類
,在類
中又包含函數
、屬性
等類型。索引
Swift提供了三種不一樣的訪問級別。這些訪問級別相對於源文件中定義的實體,同時也相對於這些源文件所屬的模塊。接口
Public
:能夠訪問本身模塊或應用中源文件裏的任何實體,別人也能夠訪問引入該模塊中源文件裏的全部實體。一般狀況下,某個接口或Framework
是能夠被任何人使用時,你能夠將其設置爲public
級別。Internal
:能夠訪問本身模塊或應用中源文件裏的任何實體,可是別人不能訪問該模塊中源文件裏的實體。一般狀況下,某個接口或Framework
做爲內部結構使用時,你能夠將其設置爲internal
級別。Private
:只能在當前源文件中使用的實體,稱爲私有實體。使用private
級別,能夠用做隱藏某些功能的實現細節。Public
爲最高級訪問級別,Private
爲最低級訪問級別。
在Swift中,訪問級別有以下使用原則:訪問級別統一性。 好比說:
public
訪問級別的變量,不能將它的類型定義爲internal
和private
的類型。由於變量能夠被任何人訪問,可是定義它的類型不能夠,因此這樣就會出現錯誤。public
而參數或者返回類型定義爲internal
或private
,就會出現函數能夠被任何人訪問,可是它的參數和返回類型不能夠,一樣會出現錯誤。代碼中的全部實體,若是你不明確的定義其訪問級別,那麼它們默認爲internal
級別。在大多數狀況下,咱們不須要明確的設置實體的訪問級別,由於咱們大多數時候都是在開發一個App bundle。
當你編寫一個單目標應用程序時,該應用的全部功能都是爲該應用服務,不須要提供給其餘應用或者模塊使用,因此咱們不須要明確設置訪問級別,使用默認的訪問級別internal
便可。可是若是你願意,你也可使用private
級別,用於隱藏一些功能的實現細節。
當你開發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 = 0internal let someInternalConstant = 0private func somePrivateFunction() {}
除非有特殊的說明,不然實體都使用默認的訪問級別internal
,能夠查閱默認訪問級別這一節。這意味着SomeInternalClass
和someInternalConstant
不用明確的使用修飾符聲明訪問級別,可是他們任然擁有隱式的訪問級別internal
:
class SomeInternalClass {}// 隱式訪問級別internalvar 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}
咱們能夠看到,這個函數的返回類型是一個元組,該元組中包含兩個自定義的類(可查閱自定義類型)。其中一個類的訪問級別是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()
常量、變量、屬性、下標索引的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
,並將初始化值設爲""
(即一個空字符串)。該結構體同時也定義了另外一個用於存儲的屬性名爲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
屬性的值,可是你不能對其進行賦值。這樣就能很好的告訴使用者,你只管使用,而不須要知道其實現細節。
咱們能夠給自定義的初始化方法指定訪問級別,可是必需要低於或等於它所屬類的訪問級別。但若是該初始化方法是必需要使用的話,那它的訪問級別就必須和所屬類的訪問級別相同。
如同函數或方法參數,初始化方法參數的訪問級別也不能低於初始化方法的訪問級別。
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
的類類型。
注意:這條規則也適用於爲知足協議一致性而給相關類型命名別名。