Swift學習筆記7--訪問控制

在Swift語言中,訪問修飾符有三種,分別爲private,internal和public。同時,Swift對於訪問權限的控制,不是基於類的,而是 基於文件的。其區別以下:

1,private
private訪問級別所修飾的屬性或者方法只能在當前的Swift源文件裏能夠訪問。
 
2,internal(默認訪問級別,internal修飾符可寫可不寫)
internal訪問級別所修飾的屬性或方法在源代碼所在的整個模塊均可以訪問。
若是是框架或者庫代碼,則在整個框架內部均可以訪問,框架由外部代碼所引用時,則不能夠訪問。
若是是App代碼,也是在整個App代碼,也是在整個App內部能夠訪問。

3,publicapp

能夠被任何人使用
 
=========================================

訪問控制

訪問控制能夠限定你在源文件或模塊中訪問代碼的級別,也就是說能夠控制哪些代碼你能夠訪問,哪些代碼你不能訪問。這個特性可讓咱們隱藏功能實現的一些細節,而且能夠明確的指定咱們提供給其餘人的接口中哪些部分是他們可使用的,哪些是他們看不到的。框架

你能夠明確的給類、結構體、枚舉、設置訪問級別,也能夠給屬性、函數、初始化方法、基本類型、下標索引等設置訪問級別。協議也能夠被限定在必定的範圍內使用,包括協議裏的全局常量、變量和函數。ide

在提供了不一樣訪問級別的同時,Swift並無規定咱們要在任什麼時候候都要在代碼中明確指定訪問級別。其實,若是咱們做爲獨立開發者在開發咱們本身的app,而不是在開發一些Framework的時候,咱們徹底能夠不用明確的指定代碼的訪問級別。函數

 

注意:爲方便起見,在代碼中能夠設置訪問級別的它們(屬性、基本類型、函數等)在下面的章節中咱們稱之爲「實體」。this

 

模塊和源文件

Swift中的訪問控制模型基於模塊和源文件這兩個概念。spa

模塊指的是FrameworkApp bundle。在Swift中,能夠用import關鍵字引入本身的工程。code

在Swift中,FramewordkApp bundle被做爲模塊處理。若是你是爲了實現某個通用的功能,或者是爲了封裝一些經常使用方法而將代碼打包成Framework,這個Framework在Swift中就被稱爲模塊。不論它被引入到某個App工程或者其餘的Framework,它裏面的一切(屬性、函數等)都屬於這個模塊。繼承

源文件指的是Swift中的Swift File,就是編寫Swift代碼的文件,它一般屬於一個模塊。一般一個源文件包含一個,在中又包含函數屬性等類型。索引

訪問級別

Swift提供了三種不一樣的訪問級別。這些訪問級別相對於源文件中定義的實體,同時也相對於這些源文件所屬的模塊。接口

  • Public:能夠訪問本身模塊或應用中源文件裏的任何實體,別人也能夠訪問引入該模塊中源文件裏的全部實體。一般狀況下,某個接口或Framework是能夠被任何人使用時,你能夠將其設置爲public級別。
  • Internal:能夠訪問本身模塊或應用中源文件裏的任何實體,可是別人不能訪問該模塊中源文件裏的實體。一般狀況下,某個接口或Framework做爲內部結構使用時,你能夠將其設置爲internal級別。
  • Private:只能在當前源文件中使用的實體,稱爲私有實體。使用private級別,能夠用做隱藏某些功能的實現細節。

Public爲最高級訪問級別,Private爲最低級訪問級別。

訪問級別的使用原則

在Swift中,訪問級別有以下使用原則:訪問級別統一性。 好比說:

  • 一個public訪問級別的變量,不能將它的類型定義爲internalprivate的類型。由於變量能夠被任何人訪問,可是定義它的類型不能夠,因此這樣就會出現錯誤。
  • 函數的訪問級別不能高於它的參數、返回類型的訪問級別。由於若是函數定義爲public而參數或者返回類型定義爲internalprivate,就會出現函數能夠被任何人訪問,可是它的參數和返回類型不能夠,一樣會出現錯誤。

默認訪問級別

代碼中的全部實體,若是你不明確的定義其訪問級別,那麼它們默認爲internal級別。在大多數狀況下,咱們不須要明確的設置實體的訪問級別,由於咱們大多數時候都是在開發一個App bundle。

單目標應用程序的訪問級別

當你編寫一個單目標應用程序時,該應用的全部功能都是爲該應用服務,不須要提供給其餘應用或者模塊使用,因此咱們不須要明確設置訪問級別,使用默認的訪問級別internal便可。可是若是你願意,你也可使用private級別,用於隱藏一些功能的實現細節。

Framework的訪問級別

當你開發Framework時,就須要把一些實體定義爲public級別,以便其餘人導入該Framework後能夠正常使用其功能。這些被你定義爲public的實體,就是這個Framework的API。

 

注意:Framework的內部實現細節依然可使用默認的internal級別,或者也能夠定義爲private級別。只有你想將它做爲API的實體,纔將其定義爲public級別。

 

訪問控制語法

經過修飾符publicinternalprivate來聲明實體的訪問級別:

public class SomePublicClass {}internal class SomeInternalClass {}private class SomePrivateClass {}public var somePublicVariable = 0internal let someInternalConstant = 0private func somePrivateFunction() {}

除非有特殊的說明,不然實體都使用默認的訪問級別internal,能夠查閱默認訪問級別這一節。這意味着SomeInternalClasssomeInternalConstant不用明確的使用修飾符聲明訪問級別,可是他們任然擁有隱式的訪問級別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}

將該函數申明爲publicinternal,或者使用默認的訪問級別internal都是錯誤的,由於若是把該函數當作publicinternal級別來使用的話,是沒法獲得private級別的返回值的。

枚舉類型

枚舉中成員的訪問級別繼承自該枚舉,你不能爲枚舉中的成員指定訪問級別。

好比下面的例子,枚舉CompassPoint被明確的申明爲public級別,那麼它的成員NorthSouthEastWest的訪問級別一樣也是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

常量、變量、屬性、下標索引的GettersSetters的訪問級別繼承自它們所屬成員的訪問級別。

Setter的訪問級別能夠低於對應的Getter的訪問級別,這樣就能夠控制變量、屬性或下標索引的讀寫權限。在varsubscript定義做用域以前,你能夠經過private(set)internal(set)先爲它門的寫權限申明一個較低的訪問級別。

 

注意:這個規定適用於用做存儲的屬性或用做計算的屬性。即便你不明確的申明存儲屬性的GetterSetter,Swift也會隱式的爲其建立GetterSetter,用於對該屬性進行讀取操做。使用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被修改的次數。這個功能的實現經過屬性valuedidSet方法實現,每當給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級別的類型別名能夠設定給一個publicinternalprivate的類型,可是一個public級別的類型別名只能設定給一個public級別的類型,不能設定給internalprivate的類類型。

 

注意:這條規則也適用於爲知足協議一致性而給相關類型命名別名。

相關文章
相關標籤/搜索