iOS9-by-Tutorials-學習筆記一:Swift-2-0

來源:互聯網
上載者:User

iOS9-by-Tutorials-學習筆記一:Swift-2-0
iOS9-by-Tutorials-學習筆記一:Swift-2-0

Apple在前段時間開源了Swift,在iOS開發領域中又製造了一陣騷動,看了一眼Swift的開發路線圖,計劃在明年的秋天發布Swift 3.0。Apple現在在Swift上變得也更加的開發,鼓勵社區貢獻代碼,也開始接納社區的一些反饋了。蘋果改變以往的封閉的姿態,表明了它對於Swift語言的重視,同時也說明了Swift語言蘋果會加大力度去最佳化,所以現在對於我們iOS開發人員來說,是時候開始學習iOS了。
前段時間也面試了幾個人,簡曆裡面好幾個都寫了精通Swift,但是一問問題好多都答不上來,簡曆上真的。。。。。更多的人貌似沒有開始學Swift,但是最後我都建議他們去學習一下Swift。
扯遠了,回到正題,這篇文章是我的學習筆記,非本人原創內容,只是在看《iOS 9 by Tutorials》這本書時候的一些筆記,然後加上自己的一些理解而已。

Swift 2中加入了幾個(作者認為)比較重要的改進,如下:
* 新的控制流程
* (相對)完善的錯誤處理模型
* 協議擴充
* 模式比對的增強
* API可用性檢測
* 其他一些。。。。。。

控制流程

在書中首先作者解釋了一下控制流程,感覺不錯:程式中任何能夠影響程式執行到不同的路徑的結構或者關鍵字都可以叫做控制流程,原文:any construct or keyword that causes the execution of your program to follow a different path can be considered “control flow”.

repeat…while

repeat…while是重複的意思,類似於其他語言中的do…while。其實在Swift 1.x中還是使用的do…while,在2.x中為了與do…catch區分,所以改成了repeat,但是語義上還是沒有變化。這裡多說一句,Swift的好多改進,都是為了讓程式讀上去更加明確,例如Optional、guard等也有這方面的考慮。

本例子中的代碼都是在Playground中實現的

var x = 2repeat {    print("x:\(x)")    x += 1 // Swift計劃在3.0中移除 ++ -- 所以還是盡量少用吧} while x < 10 // 這個地方可以添加括弧

上面while後面可以不適用括弧,這個也是Swift的一個改進,Swift中只有必要(即語義不明確)的時候才會要求必須加括弧。

guard

guard這個詞我也不知道怎麼翻譯,這裡就不翻譯了。但是這個關鍵字的作用的就是一個先決條件的檢測。先看下面的例子:

func printName(name: String) {    guard !name.isEmpty else {        print("no name")        return    }    print(name)}printName("")printName("MengXiangYue")

上面的例子是一個沒有意義的例子,只是為了示範。定義了一個函數列印傳入的名字,這個函數的要求如果傳入的name為空白,就判定程式錯誤,然後返回不執行代碼。guard 後面跟一個條件,條件為真的時候不會執行else,當條件為假的時候將會執行else,這樣就能夠達到了我們的要求。但是可能又回說,我用一個if-else也能夠實現這個功能,但是如果要是跟Optional結合在一起就比if-else方便多了,下面繼續看這個例子:

func printName(inName: String?) { // 這裡變成了可選值了    guard let name = inName else {        print("no name")        return    }    guard !name.isEmpty else {        print("no name")        return    }    print(name)}printName("")printName("MengXiangYue")

上面的例子中傳入的參數是一個可選值,這時候使用『guard let name = _name else…』,這個類似於if let解包的方式,但是看下面我們使用guard聲明的name變數,在下面是能夠正常使用的,但是考慮如果使用if let這個就不能使用了,所以我認為guard結合Optional是使用起來最方便的。另外這個東西也可以實作類別似NSAssert類似的功能,只是這個不會崩潰。

(相對)完善的錯誤處理模型

這裡我加了一個相對,主要是指的相對於Swift 1.x,2.x的錯誤處理好用了不少,但是相比於java等其他部分語言,還是不完善,Swift中的錯誤處理,對於拋出錯誤來說,你只是知道該函數拋出了錯誤,但是不清楚這個函數拋出了什麼錯誤,書中有句話寫的很正確,這個要求寫程式的時候一定要在文檔中寫明,會拋出的各種異常(在java中會明確的拋出Exception,Exception與Swift的Error功能一致)。

另外相對於Objective-C的NSError把指標傳遞進去,然後等函數執行完成之後檢查,已經先進了不少,鼓掌。。。。。
定義下面的一個協議:

{% codeblock %} swift
protocol JSONParsable {
static func parse(json: [String: AnyObject]) throws -> Self
}

<code class=" hljs coffeescript">這個協議定義了一個靜態方法,這裡不能叫做類方法,以為協議同時可以應用到Struct上,可以叫類型方法。這個函數使用了**throws** 關鍵字,這個關鍵字表示該方法可能會拋出一個錯誤,這裡也看不出來拋出什麼錯誤(你妹啊,啥錯誤都不知道),所以就更加突出這時候注釋的重要性(可以寫篇文章:論注釋的重要性,哈哈哈)。   那既然說到拋出錯誤,那我們就得定義錯誤,在Swift中定義錯誤比較容易,只要定義一個枚舉類型,然後遵守**ErrorType** 協議就可以了。OC中的NSError同樣也實現了**ErrorType** 協議,所以我們能夠在OC和Swift中使用NSError沒有問題。下面定義一個錯誤:```swiftenum ParseError: ErrorType {    case MissingAttribute(message: String)}</code>

定義一個錯誤比較簡單,跟普通的枚舉沒什麼不同,這裡定義了一個有關聯值的枚舉。關聯值這裡要多扯一句,關聯值這個東西在Swift中能夠解決好多與類型相關的東西,有時候我們經常會遇到某個類型與值相關,比如我們自己的工程中,網路請求錯誤需要帶著錯誤碼和錯誤提示,這時候我在OC中可能需要返回三個參數,但是在Swift中我可以只是返回一個枚舉,然後關聯上另外的兩個值。對於多個有關係的值,同樣也可以使用元組,曾經看kingfisher的時候,作者把一個類的配置參數都放到一個元組裡面,然後解析這個元組,這樣參數可能更加清晰。
又扯遠了,回到正題。下面我們實現一個結構體Person:

struct Person: JSONParsable {    let firstName: String    let lastName: String    static func parse(json: [String : AnyObject]) throws -> Person {        guard let firstName = json["first_name"] as? String else {            let message = "Expected first_name String"            throw ParseError.MissingAttribute(message: message) // 1        }        guard let lastName = json["last_name"] as? String else {            let message = "Expected last_name String"            throw ParseError.MissingAttribute(message: message) // 2        }        return Person(firstName: firstName, lastName: lastName)    }}

代碼比較簡單就不過多解釋了,就是在不同情況下拋出不同的異常。我們在調用這個方法的時候,需要處理這些異常,這時候就使用到了Swift中的do…catch。下面是代碼:

do {    let person = try Person.parse(["foo": "bar"])} catch ParseError.MissingAttribute(let message) {        print(message)} catch {        print("Unexpected ErrorType")}

do後面需要使用{}將拋出異常的函數包起來,調用拋出異常的方法的時候,需要使用try關鍵字,然後後面跟著需要捕獲的異常,如果清楚需要捕獲的異常的類型,可以再catch後面加上異常類型,如果沒有異常類型,那表示捕獲所有的異常。異常會按照catch的順序挨個匹配,直到找到第一個匹配的結束。

如果我們對於異常不關心,我們可以使用try?、try!調用方法,其中try?調用方法會返回一個Optional值,如果調用成功將會返回對應的結果,如果失敗則返回nil,程式一定不會崩潰,但是如果我們直接使用try!如果有異常拋出,程式將會崩潰。所以只有在保證我們調用的函數不會拋出異常的時候才能使用try!。

let p1 = try? Person.parse(["foo": "bar"])  // nillet p2 = try! Person.parse(["first_name": "Ray", "last_name": "Wenderlich"]) // Personlet p3 = try! Person.parse(["foo": "bar"]) // error crash
協議擴充

在這一部分使用一個例子來介紹協議擴充,協議擴充是在Swift 2.x中一個比較重要的思想。詳細的可以看看WWDC 2015 Session 408瞭解。下面定義一個驗證字串規則的一個協議:

protocol StringValidationRule {    func validate(string: String) throws -> Bool // 驗證是否合法的方法    var errorType: StringValidationError { get }  // error的類型}

上面定義了校正規則的協議,下面定義一個校正器協議:

protocol StringValidator {    var validationRules: [StringValidationRule] { get }    func validate(string: String) -> (valid: Bool, errors: [StringValidationError])}

StringValidator這個校正器,有一個儲存校正規則的數組,然後有一個校正方法,返回一個元祖,包含最終的校正結果,及錯誤。這裡我們考慮一下對於校正器可能我們處理的邏輯都是一樣的,就是迴圈所有的校正規則,然後查看是否校正成功。這個邏輯算是比較一致,如果我們把這個放到每個實現該協議的類型裡面,那代碼可能會重複。這時候我們可以提供一個預設的實現,這就是協議擴充(類似於虛函數的功能)。

extension StringValidator {    func validate(string: String) -> (valid: Bool, errors:[StringValidationError]) {        var errors = [StringValidationError]()        for rule in validationRules {            do {                try rule.validate(string)            } catch let error as StringValidationError {                errors.append(error)            } catch let error {                fatalError("Unexpected error type: \(error)")            }        }        return (valid: errors.isEmpty, errors: errors)    }}

下面我們實現一個字串以某些字元開始和以某些字元結束的的規則。首先定義一下上面的StringValidationError

// 錯誤類型enum StringValidationError: ErrorType {    case MustStartWith(set: NSCharacterSet, description: String)    case MustEndWith(set: NSCharacterSet, description: String)    var description: String {      let errorString: String      switch self {      case .MustStartWith(\_, let description):        errorString = "Must start with \(description)."      case .MustEndWith(\_, let description):        errorString = "Must end with \(description)."      }      return errorString    }}   // 擴充Stringextension String {    public func startsWithCharacterFromSet(set: NSCharacterSet) -> Bool {        guard !isEmpty else {            return false        }        return rangeOfCharacterFromSet(set, options: [], range: startIndex.. Bool {        guard !isEmpty else {            return false        }        return rangeOfCharacterFromSet(set, options: [], range: endIndex.predecessor().. Bool {        string        if string.startsWithCharacterFromSet(characterSet) {            return true        } else{            throw errorType // 4        }    }}struct EndsWithCharacterStringValidationRule: StringValidationRule {    let characterSet: NSCharacterSet    let description: String    var errorType: StringValidationError {        return .MustEndWith(set: characterSet, description: description)    }    func validate(string: String) throws -> Bool {        if string.endsWithCharacterFromSet(characterSet) {            return true        } else {            throw errorType        }    }}

兩個驗證規則建立好了,下面我們建立一個校正器:

// 這個校正器實現了StringValidator,但是由於StringValidator存在擴充,所以可以不用實現該協議中的func validate(string: String) -> (valid: Bool, errors:[StringValidationError])方法struct StartsAndEndsWithStringValidator: StringValidator {  let startsWithSet: NSCharacterSet  let startsWithDescription: String  let endsWithSet: NSCharacterSet  let endsWithDescription: String  var validationRules: [StringValidationRule] {    return [      StartsWithCharacterStringValidationRule(characterSet: startsWithSet, description: startsWithDescription),      EndsWithCharacterStringValidationRule(characterSet: endsWithSet, description: endsWithDescription)    ]  }}// 下面使用一下et numberSet = NSCharacterSet.decimalDigitCharacterSet()let startsAndEndsWithValidator = StartsAndEndsWithStringValidator(startsWithSet: letterSet, startsWithDescription: "letter", endsWithSet: numberSet, endsWithDescription: "number")startsAndEndsWithValidator.validate("1foo").errors.description

上面的內容是一個簡單的例子,我將書中的例子做了一些簡化。

下面我們再看一個例子,在擴充協議的時候我們可以結合where關鍵字,使符合where條件的類型,才會自動的存在預設的協議擴充。

// 擴充了MutableCollectionType協議,這個協議僅對Index為Int類型的實現了MutableCollectionType的類型生效  // Index是定義在MutableCollectionType的父協議MutableIndexable中的關聯類別型extension MutableCollectionType where Index == Int {  // 該方法任意的交換集合元素  mutating func shuffleInPlace() {    let c = self.count    for i in 0..<(c-1) {      let j = Int(arc4random_uniform(UInt32(c - i))) + i      guard i != j else { continue }      swap(&self[i], &self[j])    }  }}var people = ["Chris", "Ray", "Sam", "Jake", "Charlie"]people.shuffleInPlace()
模式比對的增強

在Swift中可以不僅可以再實現協議擴充的時候使用,還可以在for迴圈,也可以在if-let、switch、if-case的使用,如下例子:

let names = ["Charlie", "Chris", "Mic", "John", "Craig", "Felipe"]var namesThatStartWithC = [String]()// 將以"C"開頭的名字,加入到數組namesThatStartWithC中for cName in names where cName.hasPrefix("C") {  namesThatStartWithC.append(cName)}// 定義一個Authorpublic struct Author {    public let name: String    public let status: Additional_Things_PageSources.AuthorStatus    public init(name: String, status: Additional_Things_PageSources.AuthorStatus)}let authors = [  Author(name: "Chris Wagner", status: .Late(daysLate: 5)),  Author(name: "Charlie Fulton", status: .Late(daysLate: 10)),  Author(name: "Evan Dekhayser", status: .OnTime)]var slapLog = ""for author in authors {  if case .Late(let daysLate) = author.status where daysLate > 2 {    slapLog += "Ray slaps \(author.name) around a bit with a large trout \n"  }}
API可用性檢測

在Swift 2.x中檢測某個API是否可用,不用像原來一樣判斷是否能夠響應某個API,直接使用如下代碼,使其在該版本系統下生效即可:

if #available(iOS 9.0, \*) {  // 調用在iOS 9下才能使用的API}
defer關鍵字

defer在Swift中表示,在方法結束的時候一定會調用的代碼。在程式中我們經常將一些記憶體回收、狀態回複等動作放在代碼的最後,但是如果在前面代碼執行的過程中,發生了異常,那麼可能後面的代碼就不能執行,造成程式錯誤。但是使用defer關鍵字,能夠保證不管程式是否正常結束,該代碼一定會被執行。

例如在使用ATM的時候,不管使用的過程中發生了什麼異常都必須保證最後必須把銀行卡退給使用者,這個在這裡使用defer關鍵字就比較合適。

struct ATM {  mutating func dispenseFunds(amount: Float, inout account: Account) throws{   defer {  // 保證一定能夠退卡成功     log += "Card for \(account.name) has been returned to customer.\n"     ejectCard()   }   // 其他的邏輯處理 }  func ejectCard() {    // physically eject card  }}

終於是把這篇文章算是寫完了,後面的一部分都是一些小的知識點,慢慢積累吧,自己的讀書筆記,希望對別人有協助吧。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.