你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS9-by-Tutorials-學習筆記一:Swift-2-0

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

編輯:IOS開發綜合

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 = 2
repeat {
    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沒有問題。下面定義一個錯誤:

```swift
enum 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"])  // nil
let p2 = try! Person.parse(["first_name": "Ray", "last_name": "Wenderlich"]) // Person
let 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
    }
}   

// 擴展String
extension 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)
}

// 定義一個Author
public 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
  }
}

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

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved