swift 4.0> 進階知識點全面梳理(五)

1,Swift 的訪問控制模型基于模塊和源文件的概念;

2,訪問級別:

Swift 為代碼的實體提供個【五個】不同的訪問級別。

Open 訪問 和 public 訪問 允許實體被定義模塊中的任意源文件訪問,同樣可以被另一模塊的源文件通過導入該定義模塊來訪問。在指定框架的公共接口時,通常使用 open 或 public 訪問。 open 和 public 訪問 之間的區別將在之后給出;

Internal 訪問 允許實體被定義模塊中的任意源文件訪問,但不能被該模塊之外的任何源文件訪問。通常在定義應用程序或是框架的內部結構時使用。

File-private 訪問 將實體的使用限制于當前定義源文件中。當一些細節在整個文件中使用時,使用 file-private 訪問隱藏特定功能的實現細節。

private 訪問 將實體的使用限制于封閉聲明中。當一些細節僅在單獨的聲明中使用時,使用 private 訪問隱藏特定功能的實現細節。

open 訪問是最高的(限制最少)訪問級別,private 是最低的(限制最多)訪問級別。

open 訪問僅適用于類和類成員,它與 public 訪問區別如下:

public 訪問,或任何更嚴格的訪問級別的類,只能在其定義模塊中被繼承。

public 訪問,或任何更嚴格訪問級別的類成員,只能被其定義模塊的子類重寫。

open 類可以在其定義模塊中被繼承,也可在任何導入定義模塊的其他模塊中被繼承。

open 類成員可以被其定義模塊的子類重寫,也可以被導入其定義模塊的任何模塊重寫。

顯式地標記類為 open 意味著你考慮過其他模塊使用該類作為父類對代碼的影響,并且相應地設計了類的代碼。

3,單目標應用的訪問級別:

你可能會將代碼的一些部分標注為 file private 或private 以對模塊中的其他代碼隱藏它們的實現細節。

框架的訪問級別:

當你開發一個框架時,將該框架的面向公眾的接口標注為 open 或 public,這樣它就能被其他的模塊看到或訪問,比如導入該框架的應用。這個面相公眾的接口就是該框架的應用編程接口(API)。

4,你框架的任何內部實現細節仍可以使用 internal 默認訪問級別,如果你想從框架的其他部分隱藏細節也可以將它們標注為 private 或 file private 。僅當你想將它設為框架的API時你才能將實體標注為 open 或 public 。

5,單元測試目標的訪問級別

當你在寫一個有單元測試目標的應用時,你的代碼應該能被模塊訪問到以進行測試。默認情況下只有標注為 open 或 public 的才可以被其他模塊訪問。但是,如果你使用 @testable? 屬性標注了導入的生產模塊并且用使能測試的方式編譯了這個模塊,單元測試目標就能訪問任何 internal 的實體。

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

6,元組類型的訪問級別是所有類型里最嚴格的。例如,如果你將兩個不同類型的元素組成一個元組,一個元素的訪問級別是 internal,另一個是 private,那么這個元組類型是 private 級別的。元組類型不像類、結構體、枚舉和函數那樣有一個單獨的定義。元組類型的訪問級別會在使用的時候被自動推斷出來,不需要顯式指明。

7,函數類型:

函數類型的訪問級別由【函數成員類型和返回類型】中的最嚴格訪問級別決定。如果函數的計算訪問級別與上下文環境默認級別不匹配,你必須在函數定義時顯式指出。

?func someFunction() -> (SomeInternalClass, SomePrivateClass) {} 是無法通過編譯的;

這個函數的返回類型是一個由兩個在自定義類型里定義的類組成的元組。其中一個類是 “internal” 級別的,另一個是 “private”。因此,這個元組的訪問級別是“private”(元組成員的最嚴級別)。

由于返回類型是 private 級別的,你必須使用 private? 修飾符使其合法:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {}

8,枚舉類型:

枚舉中的獨立成員自動使用該枚舉類型的訪問級別。你不能給獨立的成員指明一個不同的訪問級別。

原始值和關聯值:

枚舉定義中的原始值和關聯值使用的類型必須有一個不低于枚舉的訪問級別。例如,你不能使用一個 private 類型作為一個 internal 級別的枚舉類型中的原始值類型。

9,嵌套類型:

private 級別的類型中定義的嵌套類型自動為 private 級別。fileprivate 級別的類型中定義的嵌套類型自動為 fileprivate 級別。public 或 internal 級別的類型中定義的嵌套類型自動為 internal 級別。如果你想讓嵌套類型是 public 級別的,你必須將其顯式指明為 public。

10,子類:

你可以繼承任何類只要是在當前可以訪問的上下文環境中。但子類不能高于父類的訪問級別,例如,你不能寫一個 internal 父類的 public 子類。

Getters 和 Setters:常量、變量、屬性不能擁有比它們類型更高的訪問級別。

你可以給 setter 函數一個比相對應 getter 函數更低的訪問級別以限制變量、屬性、下標的讀寫權限。你可以通過在 var 和 subscript 的置入器之前書寫 fileprivate(set) , private(set) , 或 internal(set) 來聲明更低的訪問級別。 即使你沒有給一個存儲屬性書寫一個明確的 getter 和 setter,Swift 會為你合成一個 getter 和 setter 以訪問到存儲屬性的隱式存儲。使用 fileprivate(set) , private(set) 和 internal(set) 可以改變這個合成的 setter 的訪問級別,同樣也可以改變計算屬性的訪問級別。

11,struct TrackedString {

? ? ? ? private(set) var numberOfEdits = 0

? ? ? ? var value: String = "" {

? ? ? ? didSet {

? ? ? ? ? ? ? numberOfEdits += 1

? ? ? ? } ? ? } ? ? }

TrackedString 結構體定義了一個可存儲字符串的屬性 value ,它又一個初始值 "" (空字符串)。這個結構圖同樣定義了一個可存儲整數的屬性 numberOfEdits ,它被用于記錄 value 的修改次數。這個記錄由 value 屬性中的didset屬性實現,它會增加 numberOfEdits 的值一旦 value 被設為一個新值。

TrackedString 結構體和 value 屬性都沒有顯式指出訪問級別修飾符,因此它們都遵循默認的 internal 級別。 numberOfEdits 屬性的訪問級別已經標注為 private(set) 以說明這個屬性的 getter 是默認的 internal 級別,但是這個屬性只能被 TrackedString 內的代碼設置。這允許 TrackedString 在內部修改 numberOfEdits 屬性,而且可以展示這個屬性作為一個只讀屬性當在結構體定義之外使用時——包括 TrackedString 的擴展。

12,你若有必要也可以顯式指明 getter 和 setter方法。下面的例子提供了一個定義為 public 級別的 TrackedString 結構體。結構體成員(包括 numberOfEdits 屬性)因此有一個默認的 internal 級別。你可以設置 numberOfEdits 屬性的getter方法為 public,setter 方法為 private 級別,通過結合 public 和 private(set) 訪問級別修飾符:

public struct TrackedString {

? ? public private(set) var numberOfEdits = 0

? ? ? public var value: String = "" {

? ? ? ? ? ? didSet {

? ? ? ? ? ? ? ? ? numberOfEdits += 1

? ? ? ? ? ? }

? ? ? }

? ? ? public init() {}

}

13,結構體的默認成員初始化器:

如果結構體的存儲屬性時 private 的,那么它的默認成員初始化方法就是 private 級別。如果結構體的存儲屬性是 file private 的,那么它的默認成員初始化方法就是 file private 級別。否則就是默認的 internal 級別。正如以上默認初始化的描述,如果你想在另一個模塊中使用結構體的成員初始化方法,你必須提供在定義中提供一個 public 的成員初始化方法

14,協議繼承:

如果你定義了一個繼承已有協議的協議,這個新協議最高與它繼承的協議訪問級別一致。例如你不能寫一個 public 的協議繼承一個 internal 的協議。

協議遵循:

類型可以遵循更低訪問級別的協議。例如,你可以定義一個可在其他模塊使用的 public 類型,但它就只能在定義模塊中使用如果遵循一個 internal 的協議。

遵循了協議的類的訪問級別取這個協議和該類的訪問級別的最小者。如果這個類型是 public 級別的,它所遵循的協議是 internal 級別,這個類型就是 internal 級別的。

15,高級運算符:

與 C 的算術運算符不同,Swift 中算術運算符默認不會溢出。

溢出行為都會作為錯誤被捕獲。

要允許溢出行為,可以使用 Swift 中另一套默認支持的溢出運算符,比如溢出加法運算符( &+ )。

所有這些溢出運算符都是以( & )符號開始的。

Swift 支持 C 里面所有的位運算符;

16,位取反運算符( ~ )是對所有位的數字進行取反操作; 是一個前綴運算符,需要直接放在運算符的前面,并且不能有空格:

let initialBits: UInt8 = 0b00001111

let invertedBits = ~initialBits? // equals 11110000

17,UInt8 類型的整數有八位,可以存儲 0 到 255 之間的任意值。

18,位與運算符( & )可以對兩個數的比特位進行合并。它會返回一個新的數,只有當這兩個數都是 1 的時候才能返回 1 。

位或運算符( | )可以對兩個比特位進行比較,然后返回一個新的數,只要兩個操作位任意一個為 1 時,那么對應的位數就為 1。

位異或運算符,或者說“互斥或”( ^ )可以對兩個數的比特位進行比較。它返回一個新的數,當兩個操作數的對應位不相同時,該數的對應位就為 1。

19,位左移和右移運算符:

位左移運算符( << )和位右移運算符( >> )可以把所有位數的數字向左或向右移動一個確定的位數;

位左和右移具有給整數乘以或除以二的效果。將一個數左移一位相當于把這個數翻倍,將一個數右移一位相當于把這個數減半。

20,無符號整數的移位操作,

對無符號整數的移位規則如下:

已經存在的比特位按指定的位數進行左移和右移。

任何移動超出整型存儲邊界的位都會被丟棄。

用 0 來填充向左或向右移動后產生的空白位。

這種方法稱就是所謂的邏輯移位。

21,溢出運算符:在默認情況下,當向一個整數賦超過它容量的值時,Swift 會報錯而不是生成一個無效的數。

Swift 提供三個算數溢出運算符來讓系統支持整數溢出運算。這些運算符都是以 & 開頭的:

溢出加法 ( &+ )

溢出減法 ( &- )

溢出乘法 ( &* )

22,unsignedOverflow 初始化為 UInt8 所能容納的最大整數( 255 ,二進制為 11111111 )。溢出加法運算符( &+ )對其進行加 1 操作。這使得它的二進制表示正好超出 UInt8 所能容納的位數,也就導致它溢出了邊界,如下圖所示。溢出后,留在 UInt8 邊界內的值是 00000000 ,也就是十進制數值的 0 。

23,當我們對一個無符號整數使用溢出減法( &- )進行下溢運算時也會產生類似的現象:

var unsignedOverflow = UInt8.min

unsignedOverflow = unsignedOverflow &- 1

// unsignedOverflow is now equal to 255

var signedOverflow = Int8.min

signedOverflow = signedOverflow &- 1

// signedOverflow is now equal to 127

Int8 整數能容納的最小值是 -128 ,以二進制表示即 10000000 。

對于無符號與有符號整型數值來說,當出現上溢時,它們會從數值所能容納的最大數變成最小的數。同樣地,當發生下溢時,它們會從所能容納的最小數變成最大的數。

24,struct Vector2D {

? ? var x = 0.0, y = 0.0

}

extension Vector2D {

? ? static func + (left: Vector2D, right: Vector2D) -> Vector2D {

? ? ? ? return Vector2D(x: left.x + right.x, y: left.y + right.y)

? ? }

}

25,要實現前綴或者后綴運算符,需要在聲明運算符函數的時候在 func 關鍵字[表情]之前指定 prefix 或者 postfix 限定符:

extension Vector2D {

? ? static prefix func - (vector: Vector2D) -> Vector2D {

? ? ? ? return Vector2D(x: -vector.x, y: -vector.y)

? ? }

}

這段代碼為 Vector2D? 類型實現了單目減運算符( -a )。由于單目減運算符是前綴運算符,所以這個函數需要加上 prefix? 限定符;

26,組合賦值運算符:

組合賦值運算符將賦值運算符( = )與其它運算符進行結合。比如,將加法與賦值結合成加法賦值運算符( += )。在實現的時候,需要把運算符的[表情]左參數設置成 inout? 類型,因為這個參數的值會在運算符函數內直接被修改。

extension Vector2D {

? ? static func [表情]+= (left: inout Vector2D, right: Vector2D) {

? ? ? ? left = left + right

? ? }

}

27,不能對默認的賦值運算符( = )進行重載。只有組合賦值運算符可以被重載。同樣地,也無法對三元條件運算符 a ? b : c 進行重載。

28,要使用等價運算符來檢查你自己類型的相等:

extension Vector2D {

? ? static func == (left: Vector2D, right: Vector2D) -> Bool {

? ? ? ? return (left.x == right.x) && (left.y == right.y)

? ? }

? ? static func != (left: Vector2D, right: Vector2D) -> Bool {

? ? ? ? return !(left == right)

? ? }

}

29,新的運算符要在全局作用域內,使用 operator 關鍵字進行聲明,同時還要指定 prefix 、 infix 或者 postfix 限定符:

prefix operator +++ {}

實例:

extension Vector2D {

? ? static prefix func +++ (vector: inout Vector2D) -> Vector2D {

? ? ? ? vector += vector

? ? ? ? return vector

? ? }

}

30,自定義中綴運算符的優先級和結合性:

?結合性( associativity )可取的值有 left , right? 和 none 。?

associativity 的默認值是 none , precedence 默認為 100 。下面例子定義了一個新的自定義中綴運算符 +- ,此運算符是 left 結合的,優先級為 140 :

infix operator +- { associativity left precedence 140 }

extension Vector2D {

? ? static func +- (left: Vector2D, right: Vector2D) -> Vector2D {

? ? ? ? return Vector2D(x: left.x + right.x, y: left.y - right.y)

? ? }

}

let firstVector = Vector2D(x: 1.0, y: 2.0)

let secondVector = Vector2D(x: 3.0, y: 4.0)

let plusMinusVector = firstVector +- secondVector

// plusMinusVector is a Vector2D instance with values of (4.0, -2.0)

30,協議組合類型:

協議組合類型允許你指定一個值,該值的類型遵循多個協議的要求而不必顯式定義一個新的命名型的繼承自每個你想要該類型遵循的協議的協議。比如,指定一個協議組合類型 Protocol A & Protocol B & Protocol C? 實際上是和定義一個新的繼承自 Protocol A , Protocol B? , Protocol C 的協議 Protocol D 是完全一樣的,但不需要引入一個新名字。同理,標明一個協議組合類型 SuperClass & ProtocolA 與聲明一個新類型 SubClass 繼承自 SuperClass 并遵循 ProtocolA 是一樣的,但不需要引入新名字。

31,你可以使用后綴 self 表達式來獲取類型作為一個值。比如說, SomeClass.self 返回 SomeClass 本身,而不是 SomeClas 的一個實例。并且 SomeProtocol.self 返回 SomeProtocol 本身,而不是運行時遵循 SomeProtocol 的某個類型的實例。

你可以對類型的實例使用dynamicType 表達式來獲取該實例的動態運行時的類型,如下例所示:

let someInstance: SomeBaseClass = SomeSubClass()

someInstance.dynamicType.printClassName()

// Prints "SomeSubClass"

let metatype: SomeBaseClass.Type = SomeBaseClass.self

let anotherInstance = metatype.init(string: "some string")

32,可以使用特征運算符( === 和 !== )來測試一個實例的運行時類型和它的編譯時類型是否一致。

if someInstance.dynamicType === someInstance.self { }

33,Swift 中的類型推斷作用于單獨的表達式或語句的級別。這意味所有用于推斷省略的類型或表達式中的類型所必需的信息必須可以從表達式或其子表達式中的某個表達式的類型檢查中獲取。

34,在函數中, #function 會的值就是那個函數的名字,在方法里就是那個方法的名字,在屬性設置器和讀取器中則是屬性的名字,在特殊的成員例如 init 或 subscript 就是關鍵字的名字,在最頂層文件,就就是當前模塊的名字。

父類表達式使類互動于它的父類。它有以下形式:

super. member name

super[ subscript index ]

super.init( initializer arguments )

第一種形式用來訪問父類中的成員。第二種形式用來訪問父類的下標。第三種形式用來訪問父類的初始化器。

35,通配符表達式:

(x, _) = (10, 20)

// x is 10, and 20 is ignored

36,path 可以包含使用方括號來包含下標,只要下標的形式參數遵循 Hashable 協議。這個例子在 key path 中使用下標來訪問數組中的第二個元素:

let greetings = ["hello", "hola", "bonjour", "??"]

let myGreeting = greetings[keyPath: \[String].[1]]

// myGreeting is 'hola'

37,用 objc 特性標記的類必須繼承自一個 Objective-C 中定義的類。如果你把 objc 用到類或協議中,它會隱式地應用于該類或協議中 Objective-C 兼容的成員上。如果一個類繼承自另一個帶 objc 特性標記或 Objective-C 中定義的類,編譯器也會隱式地給這個類添加 objc 特性。標記為 objc 特性的協議不能繼承自非 objc 特性的協議。

objc 特性同樣會在下面的情況中隱式地添加:

聲明是子類的重寫,并且父類的聲明有 objc 特性;

聲明滿足的需求來自一個擁有 objc 特性的協議;

聲明有 IBAction , IBOutlet , IBDesignable , IBInspectable , NSManaged , 或者 GKInspectable 特性。

38,objc 特性可以接受一個特性實參,由一個標識符組成。當你想在 Objective-C 中為 objc 特性標記的實體暴露一個不同的名字時,用這個特性。你可以把這個實參用在命名類,枚舉,枚舉成員,協議,方法,getter,setter,初始化器。下面的例子把 ExampleClass 中 enabled 屬性的getter作為 isEnabled 暴露給 Objective-C 代碼,而不僅僅是屬性本身的名字。

@objc

class ExampleClass: NSObject {

? ? var enabled: Bool {

? ? ? ? @objc(isEnabled) get {

? ? ? ? ? ? // Return the appropriate value

? ? ? ? }

? ? }

}

39,Swift 提供了下列 Interface Builde r特性: IBAction , IBOutlet , IBDesignable ,和 IBInspectable 。

40,IBAction 和 IBOutlet 特性都隱含 objc 特性。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內容