Swift 3.1新改动
##概述
Swift 3.1 和 Swift 3.0 是源碼兼容的,所以如果已經使用 Edit\Convert\To Current Swift Syntax… 將項目遷移到了 Swift 3.0 的話,新功能將不會破壞我們的代碼。不過,蘋果在 Xcode 8.3 中已經拋棄了對 Swift 2.3 的支持。所以如果還沒有從 Swift 2.3 遷移過來,現在要抓緊做了!
1. 可失敗數值轉換初始化方法
Swift 3.1 為所有數字類型 (Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Float80, Double) 實現了可失敗初始化方法,要么完全成功、不損失精度,要么返回 nil 。
例如以下處理JSON的代碼
class Student {let name: Stringlet grade: Intinit?(json: [String: Any]) {guard let name = json["name"] as? String,let gradeString = json["grade"] as? String,let gradeDouble = Double(gradeString),let grade = Int(exactly: gradeDouble) // <-- 這里是 3.1 的功能else {return nil}self.name = nameself.grade = grade} }func makeStudents(with data: Data) -> [Student] {guard let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments),let jsonArray = json as? [[String: Any]] else {return []}return jsonArray.flatMap(Student.init) }let rawStudents = "[{\"name\":\"Ray\", \"grade\":\"5.0\"}, {\"name\":\"Matt\", \"grade\":\"6\"},{\"name\":\"Chris\", \"grade\":\"6.33\"}, {\"name\":\"Cosmin\", \"grade\":\"7\"}, {\"name\":\"Steven\", \"grade\":\"7.5\"}]" let data = rawStudents.data(using: .utf8)! let students = makeStudents(with: data) dump(students) // [(name: "Ray", grade: 5), (name: "Matt", grade: 6), (name: "Cosmin", grade: 7)] 復制代碼Student 類的指定可失敗初始化方法中用可失敗構造器將 grade 屬性從 Double 轉換為 Int,就像這樣:
let grade = Int(exactly: gradeDouble) 復制代碼如果 gradeDouble 是小數,例如 6.33,就會失敗。如果可以用 Int 來表示,例如 6.0,就會成功。
2. 新的序列函數
Swift 3.1 為標準庫的 Sequence 協議增加了兩個新函數,用于數據過濾:prefix(while:) 和drop(while:)
Swift 3.1 允許我們使用 prefix(while:) 和 drop(while:) 來獲取位于序列兩個給定值之間所有的元素,像這樣:
// Swift 3.1 let interval = fibonacci.prefix(while: {$0 < 1000}).drop(while: {$0 < 100}) for element in interval {print(element) // 144 233 377 610 987 } 復制代碼prefix(while:)返回滿足某 predicate 的最長子序列。從序列的開頭開始,并且在第一個從給定閉包中返回 false 的元素處停下。
drop(while:)做相反的操作:從第一個在閉包中返回 false 的元素開始,直到序列的結束,返回此子序列。
3. Concrete Constrained Extensions
Swift 3.1 允許我們擴展具有 concrete type constraint 的泛型。之前不能擴展這樣的類型,因為約束必須是一個協議。 例如,Ruby On Rails 提供了一個非常實用的方法 isBlank 用于檢查用戶輸入。在 Swift 3.0 中我們會將其實現為 String 擴展中的計算屬性:
// Swift 3.0 extension String {var isBlank: Bool {return trimmingCharacters(in: .whitespaces).isEmpty} }let abc = " " let def = "x"abc.isBlank // true def.isBlank // false 如果想要 string 可選值 也能用 isBlank 計算屬性,在 Swift 3.0 中要這么做:// Swift 3.0 protocol StringProvider {var string: String {get} }extension String: StringProvider {var string: String {return self} }extension Optional where Wrapped: StringProvider {var isBlank: Bool {return self?.string.isBlank ?? true} }let foo: String? = nil let bar: String? = " " let baz: String? = "x"foo.isBlank // true bar.isBlank // true baz.isBlank // false 復制代碼我們創建了一個 StringProvider 協議供 String 采用。當拆包類型是StringProvider的時候使用它擴展 Optional,添加 isBlank方法。
Swift 3.1 可以用這樣的協議來擴展 concrete type:
// Swift 3.1 extension Optional where Wrapped == String {var isBlank: Bool {return self?.isBlank ?? true} } 復制代碼4. 泛型嵌套
Swift 3.1 讓我們可以混合使用泛型和類型嵌套。練習一下,看看這個(不是很難的)例子。如果某個 raywenderlich.com 的團隊領導想要在博客上發一篇文章,他會找專門的開發者團隊來處理這個問題,以保證文章的質量:
class Team<T> {enum TeamType {case swiftcase iOScase macOS}class BlogPost<T> {enum BlogPostType {case tutorialcase article}let title: Tlet type: BlogPostTypelet category: TeamTypelet publishDate: Dateinit(title: T, type: BlogPostType, category: TeamType, publishDate: Date) {self.title = titleself.type = typeself.category = categoryself.publishDate = publishDate}}let type: TeamTypelet author: Tlet teamLead: Tlet blogPost: BlogPost<T>init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost<T>) {self.type = typeself.author = authorself.teamLead = teamLeadself.blogPost = blogPost} } 復制代碼我們把內部類?BlogPost嵌套在對應的外部類Team中,這兩個類都是泛型。目前團隊在尋找已發布的教程和文章時需要這樣做:
Team(type: .swift, author: "Cosmin Pup?z?", teamLead: "Ray Fix", blogPost: Team.BlogPost(title: "Pattern Matching", type: .tutorial, category: .swift, publishDate: Date()))Team(type: .swift, author: "Cosmin Pup?z?", teamLead: "Ray Fix", blogPost: Team.BlogPost(title: "What's New in Swift 3.1?", type: .article, category: .swift, publishDate: Date())) 復制代碼但實際上可以簡化這里的代碼。如果嵌套的內部類型用了外部的泛型,它就默認繼承了父類的類型。因此我們不需要聲明,只要這樣寫就可以了:
class Team<T> {// 本來的代碼class BlogPost {// 本來的代碼} // 本來的代碼let blogPost: BlogPostinit(type: TeamType, author: T, teamLead: T, blogPost: BlogPost) {// 本來的代碼 } } 復制代碼注意:如果想學習 Swift 中的泛型,讀一讀這篇最近更新的教程?getting started with Swift generics?。
5. Swift 版本可用性
我們可以使用 Swift 版本的#if swift(>= N) ?靜態構造器,像這樣:
// Swift 3.0 #if swift(>=3.1)func intVersion(number: Double) -> Int? {return Int(exactly: number)} #elseif swift(>=3.0)func intVersion(number: Double) -> Int {return Int(number)} #endif 復制代碼然而在使用 Swift 標準庫這樣的東西時,這種方法有一個很大的缺點。它需要為每個舊語言版本編譯標準庫。因為如果要使用 Swift 3.0 的行為,則需要使用針對該版本編譯的標準庫。如果使用 3.1 版本的標準庫,就根本沒有正確的代碼。 所以,Swift 3.1 擴展了?@available屬性
// Swift 3.1(swift 3.1) func intVersion(number: Double) -> Int? {return Int(exactly: number) }(swift, introduced: 3.0, obsoleted: 3.1) func intVersion(number: Double) -> Int {return Int(number) } 復制代碼這個新功能與 intVersion 方法相同。但是,它只允許像標準庫這樣的庫被編譯一次。編譯器隨后只要選擇與對應版本兼容的功能即可。
**注意:**如果想學習 Swift 的?availability attributes,看看這篇教程?availability attributes in Swift。
6. Swift 包管理器的更新
- Editable Packages Swift 3.1 在?Swift 包管理器?中新增了?Editable Packages?概念 swift package edit命令可以將現有包轉換為可編輯的。可編輯的包將會替換?dependency graph?中的規范包。使用?—end-edit ?命令將包管理器還原回規范解析的包。
- Version Pinning Swift 3.1 在特定版本的?Swift 包管理器?中新增了?version pinning?概念。?pin命令會像這樣固定一個或多個依賴:
使用?unpin ?命令恢復到以前的包版本:
$ swift package unpin —all$ swift package unpin Foo 復制代碼包管理器在?Package.pins?中存儲每個包的活躍版本 pin 信息。如果文件不存在,包管理器則會按照包 manifest 中指定的要求自動創建該文件,這是?automatic pinning?過程的一部分。
- 其它 swift package reset命令將包重置為干凈狀態,不會檢出當前的依賴關系或 build artifact。還有,使用?swift test --parallel命令并行執行測試。
==
參考文獻 英文原版:What’s New in Swift 3.1? 一篇文章幫你徹底了解 Swift 3.1 的新內容 感謝翻譯?
總結
以上是生活随笔為你收集整理的Swift 3.1新改动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从JavaScript函数重名看其初始化
- 下一篇: css知多少(1)——我来问你来答(转)