概述
Swift 3.1 和 Swift 3.0 是源碼兼容的,所以如果已經(jīng)使用 Edit\Convert\To Current Swift Syntax… 將項目遷移到了 Swift 3.0 的話,新功能將不會破壞我們的代碼盒粮。不過辆影,蘋果在 Xcode 8.3 中已經(jīng)拋棄了對 Swift 2.3 的支持圃泡。所以如果還沒有從 Swift 2.3 遷移過來,現(xiàn)在要抓緊做了物臂!
1. 可失敗數(shù)值轉(zhuǎn)換初始化方法
Swift 3.1 為所有數(shù)字類型 (Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Float80, Double) 實現(xiàn)了可失敗初始化方法,要么完全成功产上、不損失精度棵磷,要么返回 nil 。
例如以下處理JSON的代碼
class Student {
let name: String
let grade: Int
init?(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 = name
self.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 類的指定可失敗初始化方法中用可失敗構(gòu)造器將 grade 屬性從 Double 轉(zhuǎn)換為 Int晋涣,就像這樣:
```swift
let grade = Int(exactly: gradeDouble)```
如果 gradeDouble 是小數(shù)仪媒,例如 6.33,就會失敗姻僧。如果可以用 Int 來表示规丽,例如 6.0蒲牧,就會成功。
##2. 新的序列函數(shù)
Swift 3.1 為標(biāo)準(zhǔn)庫的 Sequence 協(xié)議增加了兩個新函數(shù)赌莺,用于數(shù)據(jù)過濾:`prefix(while:)` 和` drop(while:)`
Swift 3.1 允許我們使用 prefix(while:) 和 drop(while:) 來獲取位于序列兩個給定值之間所有的元素冰抢,像這樣:
```swift
// 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 的元素開始,直到序列的結(jié)束巢音,返回此子序列遵倦。
##3. Concrete Constrained Extensions
Swift 3.1 允許我們擴展具有 concrete type constraint 的泛型。之前不能擴展這樣的類型官撼,因為約束必須是一個協(xié)議梧躺。
例如,Ruby On Rails 提供了一個非常實用的方法 isBlank 用于檢查用戶輸入傲绣。在 Swift 3.0 中我們會將其實現(xiàn)為 String 擴展中的計算屬性:
```swift
// 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```
我們創(chuàng)建了一個 StringProvider 協(xié)議供 String 采用。當(dāng)拆包類型是` StringProvider `的時候使用它擴展 Optional秃诵,添加 `isBlank `方法续搀。
Swift 3.1 可以用這樣的協(xié)議來擴展 concrete type:
```swift
// Swift 3.1
extension Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}```
##4. 泛型嵌套
Swift 3.1 讓我們可以混合使用泛型和類型嵌套。練習(xí)一下菠净,看看這個(不是很難的)例子禁舷。如果某個 raywenderlich.com 的團隊領(lǐng)導(dǎo)想要在博客上發(fā)一篇文章,他會找專門的開發(fā)者團隊來處理這個問題毅往,以保證文章的質(zhì)量:
```swift
class Team<T> {
enum TeamType {
case swift
case iOS
case macOS
}
class BlogPost<T> {
enum BlogPostType {
case tutorial
case article
}
let title: T
let type: BlogPostType
let category: TeamType
let publishDate: Date
init(title: T, type: BlogPostType, category: TeamType, publishDate: Date) {
self.title = title
self.type = type
self.category = category
self.publishDate = publishDate
}
}
let type: TeamType
let author: T
let teamLead: T
let blogPost: BlogPost<T>
init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost<T>) {
self.type = type
self.author = author
self.teamLead = teamLead
self.blogPost = blogPost
}
}```
我們把內(nèi)部類 `BlogPost`嵌套在對應(yīng)的外部類`Team`中牵咙,這兩個類都是泛型。目前團隊在尋找已發(fā)布的教程和文章時需要這樣做:
```swift
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()))```
但實際上可以簡化這里的代碼煞抬。如果嵌套的內(nèi)部類型用了外部的泛型霜大,它就默認(rèn)繼承了父類的類型构哺。因此我們不需要聲明革答,只要這樣寫就可以了:
```swift
class Team<T> {
// 本來的代碼
class BlogPost {
// 本來的代碼
}
// 本來的代碼
let blogPost: BlogPost
init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost) {
// 本來的代碼
}
}```
>**注意:**如果想學(xué)習(xí) Swift 中的**泛型**,讀一讀這篇最近更新的教程 [getting started with Swift generics](https://www.raywenderlich.com/154371/swift-generics-tutorial-getting-started) 曙强。
##5. Swift 版本可用性
我們可以使用 Swift 版本的` #if swift(>= N)`
**靜態(tài)構(gòu)造器**残拐,像這樣:
```swift
// 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 標(biāo)準(zhǔn)庫這樣的東西時,這種方法有一個很大的缺點碟嘴。它需要為每個舊語言版本編譯標(biāo)準(zhǔn)庫溪食。因為如果要使用 Swift 3.0 的行為,則需要使用針對該版本編譯的標(biāo)準(zhǔn)庫娜扇。如果使用 3.1 版本的標(biāo)準(zhǔn)庫错沃,就根本沒有正確的代碼栅组。
所以,Swift 3.1 擴展了 `@available`屬性
```swift
// Swift 3.1
@available(swift 3.1)
func intVersion(number: Double) -> Int? {
return Int(exactly: number)
}
@available(swift, introduced: 3.0, obsoleted: 3.1)
func intVersion(number: Double) -> Int {
return Int(number)
}```
這個新功能與 `intVersion` 方法相同枢析。但是玉掸,它只允許像標(biāo)準(zhǔn)庫這樣的庫被編譯一次。編譯器隨后只要選擇與對應(yīng)版本兼容的功能即可醒叁。
>**注意:**如果想學(xué)習(xí) Swift 的 **availability attributes**司浪,看看這篇教程 [availability attributes in Swift](https://www.raywenderlich.com/139077/availability-attributes-swift)。
##6. Swift 包管理器的更新
- **Editable Packages**
Swift 3.1 在 [Swift 包管理器](https://github.com/apple/swift-package-manager) 中新增了 **Editable Packages** 概念
`swift package edit`命令可以將現(xiàn)有包轉(zhuǎn)換為可編輯的把沼“∫祝可編輯的包將會替換 **dependency graph** 中的規(guī)范包。使用 `—end-edit`
命令將包管理器還原回**規(guī)范解析的包**饮睬。
- **Version Pinning**
Swift 3.1 在特定版本的 [Swift 包管理器](https://github.com/apple/swift-package-manager) 中新增了 **version pinning** 概念租谈。 `pin`命令會像這樣固定一個或多個依賴:
$ swift package pin --all // pin 所有依賴
$ swift package pin Foo // 把 Foo pin 在當(dāng)前解析版本
$ swift package pin Foo --version 1.2.3 // 把 Foo pin 在 1.2.3```
使用 unpin
命令恢復(fù)到以前的包版本:
$ swift package unpin —all$ swift package unpin Foo
包管理器在 Package.pins 中存儲每個包的活躍版本 pin 信息。如果文件不存在捆愁,包管理器則會按照包 manifest 中指定的要求自動創(chuàng)建該文件垦垂,這是 automatic pinning 過程的一部分。
-
其它
swift package reset
命令將包重置為干凈狀態(tài)牙瓢,不會檢出當(dāng)前的依賴關(guān)系或 build artifact劫拗。還有,使用swift test --parallel
命令并行執(zhí)行測試矾克。
==
參考文獻(xiàn)
英文原版:What’s New in Swift 3.1?
一篇文章幫你徹底了解 Swift 3.1 的新內(nèi)容 感謝翻譯??