swift學習筆記5——其它部分(自動引用計數、錯誤處理、泛型...)

以前學習swift時的我的筆記,根據github:the-swift-programming-language-in-chinese學習、總結,將重要的內容提取,加以理解後整理爲學習筆記,方便之後查詢用。詳細能夠參考the-swift-programming-language-in-chinese,或者蘋果官方英文版文檔html

當前版本是swift2.2ios

自動引用計數

引用計數僅僅應用於類的實例。結構體和枚舉類型是值類型,不是引用類型,也不是經過引用的方式存儲和傳遞git

當你每次建立一個類的新的實例的時候,ARC 會分配一塊內存來儲存該實例信息。內存中會包含實例的類型信息,以及這個實例全部相關的存儲型屬性的值。
然而,當 ARC 收回和釋放了正在被使用中的實例,該實例的屬性和方法將不能再被訪問和調用。實際上,若是你試圖訪問這個實例,你的應用程序極可能會崩潰。github

爲了確保使用中的實例不會被銷燬,ARC 會跟蹤和計算每個實例正在被多少屬性,常量和變量所引用。哪怕實例的引用數爲1,ARC都不會銷燬這個實例。swift

爲了使上述成爲可能,不管你將實例賦值給屬性、常量或變量,它們都會建立此實例的強引用。之因此稱之爲「強」引用,是由於它會將實例緊緊地保持住,只要強引用還在,實例是不容許被銷燬的。app

弱引用必須被聲明爲變量,代表其值能在運行時被修改。弱引用不能被聲明爲常量。框架

在使用垃圾收集的系統裏,弱指針有時用來實現簡單的緩衝機制,由於沒有強引用的對象只會在內存壓力觸發垃圾收集時才被銷燬。可是在 ARC 中,一旦值的最後一個強引用被移除,就會被當即銷燬,這致使弱引用並不適合上面的用途。即,ARC中weak對象不會被緩衝,會當即釋放ide

無主引用

和弱引用相似,無主引用不會緊緊保持住引用的實例。和弱引用不一樣的是,無主引用是永遠有值的。所以,無主引用老是被定義爲非可選類型(non-optional type)。你能夠在聲明屬性或者變量時,在前面加上關鍵字unowned表示這是一個無主引用。函數

若是你試圖在實例被銷燬後,訪問該實例的無主引用,會觸發運行時錯誤。使用無主引用,你必須確保引用始終指向一個未銷燬的實例。
還須要注意的是若是你試圖訪問實例已經被銷燬的無主引用,Swift 確保程序會直接崩潰,而不會發生沒法預期的行爲。因此你應當避免這樣的事情發生。post

可選鏈式調用

調用結果返回一個可選值

可選鏈式調用(Optional Chaining)是一種能夠在當前值可能爲nil的可選值上請求和調用屬性、方法及下標的方法。若是可選值有值,那麼調用就會成功;若是可選值是nil,那麼調用將返回nil。多個調用能夠鏈接在一塊兒造成一個調用鏈,若是其中任何一個節點爲nil,整個調用鏈都會失敗,即返回nil

Swift 的可選鏈式調用和 Objective-C 中向nil發送消息有些相像,可是 Swift 的可選鏈式調用能夠應用於任意類型,而且能檢查調用是否成功

給可選鏈式調用賦值時,若是左邊爲nil,則右邊的不會執行,好比john.residence?.address = createAddress(),若是residence爲nil則createAddress函數不會被執行

經過可選鏈式調用調用方法

一個沒有返回值的方法具備隱式的返回類型Void。這意味着沒有返回值的方法也會返回(),或者說空的元組。若是在可選值上經過可選鏈式調用來調用這個方法,該方法的返回類型會是Void?,而不是Void,由於經過可選鏈式調用獲得的返回值都是可選的。

if john.residence?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}

一樣的,能夠據此判斷經過可選鏈式調用爲屬性賦值是否成功

if (john.residence?.address = someAddress) != nil {
    print("It was possible to set the address.")
} else {
    print("It was not possible to set the address.")
}
  • 若是你訪問的值不是可選的,可選鏈式調用將會返回可選值。
  • 若是你訪問的值就是可選的,可選鏈式調用不會讓可選返回值變得「更可選」。

所以:

  • 經過可選鏈式調用訪問一個Int值,將會返回Int?,不管使用了多少層可選鏈式調用。
  • 相似的,經過可選鏈式調用訪問Int?值,依舊會返回Int?值,並不會返回Int??。

錯誤處理

用 throwing 函數傳遞錯誤

爲了表示一個函數、方法或構造器能夠拋出錯誤,在函數聲明的參數列表以後加上throws關鍵字。一個標有throws關鍵字的函數被稱做throwing 函數。若是這個函數指明瞭返回值類型,throws關鍵詞須要寫在箭頭(->)的前面。

func canThrowErrors() throws -> String

指定清理操做

可使用defer語句在即將離開當前代碼塊時執行一系列語句。該語句讓你能執行一些必要的清理工做,不論是以何種方式離開當前代碼塊的——不管是因爲拋出錯誤而離開,仍是因爲諸如return或者break的語句。例如,你能夠用defer語句來確保文件描述符得以關閉,以及手動分配的內存得以釋放。

defer語句將代碼的執行延遲到當前的做用域退出以前。該語句由defer關鍵字和要被延遲執行的語句組成。延遲執行的語句不能包含任何控制轉移語句,例如break或是return語句,或是拋出一個錯誤。延遲執行的操做會按照它們被指定時的順序的相反順序執行——也就是說,第一條defer語句中的代碼會在第二條defer語句中的代碼被執行以後才執行,以此類推。

func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // 處理文件。
        }
        // close(file) 會在這裏被調用,即做用域的最後。
    }
}

上面的代碼使用一條defer語句來確保open(:)函數有一個相應的對close(:)函數的調用。

類型轉換

檢查類型(Checking Type)

用類型檢查操做符(is)來檢查一個實例是否屬於特定子類型。若實例屬於那個子類型,類型檢查操做符返回 true,不然返回 false。

向下轉型(Downcasting)

某類型的一個常量或變量可能在幕後實際上屬於一個子類。當肯定是這種狀況時,你能夠嘗試向下轉到它的子類型,用類型轉換操做符(as? 或 as!)。

當你不肯定向下轉型能夠成功時,用類型轉換的條件形式(as?)。條件形式的類型轉換老是返回一個可選值(optional value),而且若下轉是不可能的,可選值將是 nil。這使你可以檢查向下轉型是否成功。

只有你能夠肯定向下轉型必定會成功時,才使用強制形式(as!)。當你試圖向下轉型爲一個不正確的類型時,強制形式的類型轉換會觸發一個運行時錯誤。

Any 和 AnyObject 的類型轉換

Swift 爲不肯定類型提供了兩種特殊的類型別名:

AnyObject 能夠表示任何類類型的實例。
Any 能夠表示任何類型,包括函數類型。

泛型(Generics)

泛型函數

泛型函數能夠適用於任何類型,下面的 swapTwoValues(_:_:) 函數是上面三個函數的泛型版本:

func swapTwoValues<T>(inout a: T, inout _ b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

另一個不一樣之處在於這個泛型函數名(swapTwoValues(_:_:))後面跟着佔位類型名(T),並用尖括號括起來( )。這個尖括號告訴 Swift 那個 T 是 swapTwoValues(_:_:)函數定義內的一個佔位類型名,所以 Swift 不會去查找名爲 T 的實際類型。

命名類型參數

在大多數狀況下,類型參數具備一個描述性名字,例如 Dictionary<Key, Value> 中的 Key 和 Value,以及 Array 中的 Element,這能夠告訴閱讀代碼的人這些類型參數和泛型函數之間的關係。然而,當它們之間沒有有意義的關係時,一般使用單個字母來命名,例如 T、U、V,正如上面演示的 swapTwoValues(_:_:) 函數中的 T 同樣。

  • 泛型實現棧
struct Stack<Element> {
    var items = [Element]()
    mutating func push(item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

你能夠經過在尖括號中寫出棧中須要存儲的數據類型來建立並初始化一個 Stack 實例。例如,要建立一個 String 類型的棧,能夠寫成 Stack ():

var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
// 棧中如今有 4 個字符串

擴展一個泛型類型

當你擴展一個泛型類型的時候,你並不須要在擴展的定義中提供類型參數列表。原始類型定義中聲明的類型參數列表在擴展中能夠直接使用,而且這些來自原始類型中的參數名稱會被用做原始定義中類型參數的引用。

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

類型約束語法

你能夠在一個類型參數名後面放置一個類名或者協議名,並用冒號進行分隔,來定義類型約束,它們將成爲類型參數列表的一部分。對泛型函數添加類型約束的基本語法以下所示(做用於泛型類型時的語法與之相同):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 這裏是泛型函數的函數體部分
}

上面這個函數有兩個類型參數。第一個類型參數 T,有一個要求 T 必須是 SomeClass 子類的類型約束;第二個類型參數 U,有一個要求 U 必須符合 SomeProtocol 協議的類型約束。

訪問控制(Access Control)

訪問級別
Swift 爲代碼中的實體提供了三種不一樣的訪問級別。這些訪問級別不只與源文件中定義的實體相關,同時也與源文件所屬的模塊相關。

  • public:能夠訪問同一模塊源文件中的任何實體,在模塊外也能夠經過導入該模塊來訪問源文件裏的全部實體。一般狀況下,框架中的某個接口能夠被任何人使用時,你能夠將其設置爲 public 級別。
  • internal:能夠訪問同一模塊源文件中的任何實體,可是不能從模塊外訪問該模塊源文件中的實體。一般狀況下,某個接口只在應用程序或框架內部使用時,你能夠將其設置爲 internal 級別。
  • private:限制實體只能在所在的源文件內部使用。使用 private 級別能夠隱藏某些功能的實現細節。

public 爲最高(限制最少)訪問級別,private 爲最低(限制最多)訪問級別。默認爲internal

一個類型的訪問級別也會影響到類型成員(屬性、方法、構造器、下標)的默認訪問級別。若是你將類型指定爲 private 級別,那麼該類型的全部成員的默認訪問級別也會變成 private。若是你將類型指定爲 public 或者 internal 級別(或者不明確指定訪問級別,而使用默認的 internal 訪問級別),那麼該類型的全部成員的默認訪問級別將是 internal。

上面提到,一個 public 類型的全部成員的訪問級別默認爲 internal 級別,而不是 public 級別。若是你想將某個成員指定爲 public 級別,那麼你必須顯式指定。這樣作的好處是,在你定義公共接口的時候,能夠明確地選擇哪些接口是須要公開的,哪些是內部使用的,避免不當心將內部使用的接口公開。

訪問級別基本原則

Swift 中的訪問級別遵循一個基本原則:不能夠在某個實體中定義訪問級別更高的實體。

例如:

  • 一個 public 訪問級別的變量,其類型的訪問級別不能是 internal 或 private。由於沒法保證變量的類型在使用變量的地方也具備訪問權限。
  • 函數的訪問級別不能高於它的參數類型和返回類型的訪問級別。由於若是函數定義爲 public 而參數類型或者返回類型定義爲 internal 或 private,就會出現函數能夠在任何地方被訪問,可是它的參數類型和返回類型卻不能夠。

框架的訪問級別

當你開發框架時,就須要把一些對外的接口定義爲 public 級別,以便使用者導入該框架後能夠正常使用其功能。這些被你定義爲 public 的接口,就是這個框架的 API。

單元測試 target 的訪問級別

當你的應用程序包含單元測試 target 時,爲了測試,測試模塊須要訪問應用程序模塊中的代碼。默認狀況下只有 public 級別的實體才能夠被其餘模塊訪問。然而,若是在導入應用程序模塊的語句前使用 @testable 特性,而後在容許測試的編譯設置(Build Options -> Enable Testability)下編譯這個應用程序模塊,單元測試 target 就能夠訪問應用程序模塊中全部 internal 級別的實體。

元組類型

元組的訪問級別將由元組中訪問級別最嚴格的類型來決定。例如,若是你構建了一個包含兩種不一樣類型的元組,其中一個類型爲 internal 級別,另外一個類型爲 private 級別,那麼這個元組的訪問級別爲 private。

元組不一樣於類、結構體、枚舉、函數那樣有單獨的定義。元組的訪問級別是在它被使用時自動推斷出的,而沒法明確指定。

函數類型

函數的訪問級別根據訪問級別最嚴格的參數類型或返回類型的訪問級別來決定。若是這種推斷出來的訪問級別不符合函數定義所在環境的默認訪問級別,那麼就須要明確地指定該函數的訪問級別。並且函數訪問級別不能高於訪問的參數

枚舉類型

枚舉成員的訪問級別和該枚舉類型相同,你不能爲枚舉成員單獨指定不一樣的訪問級別。

  • 原始值和關聯值

枚舉定義中的任何原始值或關聯值的類型的訪問級別至少不能低於枚舉類型的訪問級別。例如,你不能在一個 internal 訪問級別的枚舉中定義 private 級別的原始值類型。

嵌套類型

若是在 private 級別的類型中定義嵌套類型,那麼該嵌套類型就自動擁有 private 訪問級別。若是在 public 或者 internal 級別的類型中定義嵌套類型,那麼該嵌套類型自動擁有 internal 訪問級別。若是想讓嵌套類型擁有 public 訪問級別,那麼須要明確指定該嵌套類型的訪問級別。

子類

子類的訪問級別不得高於父類的訪問級別。例如,父類的訪問級別是 internal,子類的訪問級別就不能是 public。

Getter 和 Setter

常量、變量、屬性、下標的 Getters 和 Setters 的訪問級別和它們所屬類型的訪問級別相同。

構造器

自定義構造器的訪問級別能夠低於或等於其所屬類型的訪問級別。惟一的例外是必要構造器,它的訪問級別必須和所屬類型的訪問級別相同。

如同函數或方法的參數,構造器參數的訪問級別也不能低於構造器自己的訪問級別。(要麼改函數,要麼改參數的訪問級別)

默認構造器的訪問級別與所屬類型的訪問級別相同,除非類型的訪問級別是 public。若是一個類型被指定爲 public 級別,那麼默認構造器的訪問級別將爲 internal

協議

協議中的每個要求都具備和該協議相同的訪問級別。你不能將協議中的要求設置爲其餘訪問級別。這樣才能確保該協議的全部要求對於任意採納者都將可用。

若是你定義了一個 public 訪問級別的協議,那麼該協議的全部實現也會是 public 訪問級別

協議一致性

一個類型能夠採納比自身訪問級別低的協議。例如,你能夠定義一個 public 級別的類型,它能夠在其餘模塊中使用,同時它也能夠採納一個 internal 級別的協議,可是隻能在該協議所在的模塊中做爲符合該協議的類型使用。

採納了協議的類型的訪問級別取它自己和所採納協議二者間最低的訪問級別。也就是說若是一個類型是 public 級別,採納的協議是 internal 級別,那麼採納了這個協議後,該類型做爲符合協議的類型時,其訪問級別也是 internal。

若是你採納了協議,那麼實現了協議的全部要求後,你必須確保這些實現的訪問級別不能低於協議的訪問級別。例如,一個 public 級別的類型,採納了 internal 級別的協議,那麼協議的實現至少也得是 internal 級別。

Swift 和 Objective-C 同樣,協議的一致性是全局的,也就是說,在同一程序中,一個類型不可能用兩種不一樣的方式實現同一個協議。

擴展

你能夠在訪問級別容許的狀況下對類、結構體、枚舉進行擴展。擴展成員具備和原始類型成員一致的訪問級別。例如,你擴展了一個 public 或者 internal 類型,擴展中的成員具備默認的 internal 訪問級別,和原始類型中的成員一致 。若是你擴展了一個 private 類型,擴展成員則擁有默認的 private 訪問級別。

或者,你能夠明確指定擴展的訪問級別(例如,private extension),從而給該擴展中的全部成員指定一個新的默認訪問級別。這個新的默認訪問級別仍然能夠被單獨指定的訪問級別所覆蓋。

經過擴展添加協議一致性

若是你經過擴展來採納協議,那麼你就不能顯式指定該擴展的訪問級別了。協議擁有相應的訪問級別,並會爲該擴展中全部協議要求的實現提供默認的訪問級別。

泛型

泛型類型或泛型函數的訪問級別取決於泛型類型或泛型函數自己的訪問級別,還需結合類型參數的類型約束的訪問級別,根據這些訪問級別中的最低訪問級別來肯定。

類型別名

你定義的任何類型別名都會被看成不一樣的類型,以便於進行訪問控制。類型別名的訪問級別不可高於其表示的類型的訪問級別。例如,private 級別的類型別名能夠做爲 public、internal、private 類型的別名,可是 public 級別的類型別名只能做爲 public 類型的別名,不能做爲 internal 或 private 類型的別名。

高級運算符(Advanced Operators)

與 C 語言中的算術運算符不一樣,Swift 中的算術運算符默認是不會溢出的。全部溢出行爲都會被捕獲並報告爲錯誤。若是想讓系統容許溢出行爲,能夠選擇使用 Swift 中另外一套默認支持溢出的運算符,好比溢出加法運算符(&+)。全部的這些溢出運算符都是以 & 開頭的。

對無符號整數進行移位的規則以下:

  • 已經存在的位按指定的位數進行左移和右移。
  • 任何因移動而超出整型存儲範圍的位都會被丟棄。
  • 用 0 來填充移位後產生的空白位。

有符號

  • 當對整數進行按位右移運算時,遵循與無符號整數相同的規則,可是對於移位產生的空白位使用符號位進行填充,而不是用 0。

運算符函數(運算符重載)

單目運算符只運算一個值。當運算符出如今值以前時,它就是前綴的(例如 -a),而當它出如今值以後時,它就是後綴的(例如 b!) a+b,+爲中綴運算符

不能對默認的賦值運算符(=)進行重載。只有組合賦值運算符能夠被重載。一樣地,也沒法對三目條件運算符 (a ? b : c) 進行重載。

等價運算符

自定義的類和結構體沒有對等價運算符進行默認實現,等價運算符一般被稱爲「相等」運算符(==)與「不等」運算符(!=)。對於自定義類型,Swift 沒法判斷其是否「相等」,由於「相等」的含義取決於這些自定義類型在你的代碼中所扮演的角色。

自定義運算符
除了實現標準運算符,在 Swift 中還能夠聲明和實現自定義運算符。能夠用來自定義運算符的字符列表請參考運算符。

新的運算符要使用 operator 關鍵字在全局做用域內進行定義,同時還要指定 prefix、infix 或者 postfix 修飾符:

prefix operator +++ {}
相關文章
相關標籤/搜索