Swift 4.1 新特性概覽

知識小集發表於2018-04-04

蘋果爸爸在 3.29 正式釋出了 Swift 4.1 版本,這個版本從程式碼層面相容了 Swift 4,所以如果用 Xcode 中的 Swift Migrator 來遷移工程的話,不會影響到原來的程式碼。

本文主要整理了 raywenderlich 上的 What’s New in Swift 4.1? 和 官方部落格上的內容,簡要介紹了 Swift 4.1 相關的一些新特性。

1 支援元素型別為 Optional 的集合比較,或者底層型別為 OptionalOptional 比較,只要內層 Optional 的底層型別實現了 Equatable 協議。[SE-0143]

let array1 = [1, nil, 2, nil, 3, nil]
let array2 = [1, nil, 2, nil, 3, nil]
array1 == array2        // true

let optional1: Optional<Optional<Int>> = 1
let optional2: Optional<Optional<Int>> = 1
optional1 == optional2  // true
複製程式碼

2 支援多維陣列直接比較,只要底層型別實現了 Equatable 協議。[SE-0143]

let arrayOfArray1 = [[1, 2, 3]]
let arrayOfArray2 = [[1, 2, 3]]
arrayOfArray1 == arrayOfArray2  // true
複製程式碼

3 如果集合(Array, Dictionary, Set)和 Optional 的元素符合 Codable 協議,則集合和 Optional 本身也符合 Codable 協議。

let cosmin = Student(firstName: "Cosmin", grade: 10)
let george = Student(firstName: "George", grade: 9)
let encoder = JSONEncoder()

let students = [cosmin, george]
do {
    try encoder.encode(students)
} catch {
    print("Failed encoding students array: \(error)")
}
複製程式碼

4JSON 編解碼時,可以在 物件/結構體的 Camel Case 格式的屬性名和 JSONSnake Case 格式的 key 之間轉換,只需要設定編解碼物件的 keyEncodingStrategy 屬性。

let cosmin = Student(firstName: "Cosmin", grade: 10)
let george = Student(firstName: "George", grade: 9)
let encoder = JSONEncoder()

let students = [cosmin, george]

var jsonData = Data()
encoder.keyEncodingStrategy = .convertToSnakeCase
encoder.outputFormatting = .prettyPrinted

do {
    jsonData = try encoder.encode(students)
} catch {
    print(error)
}

if let jsonString = String(data: jsonData, encoding: .utf8) {
    print(jsonString)
}

//[
//    {
//        "grade" : 10,
//        "first_name" : "Cosmin"
//    },
//    {
//        "grade" : 9,
//        "first_name" : "George"
//    }
//]
複製程式碼

JSON 字串解碼為物件/結構體時,也有類似的操作。

5 struct 如果要符合 EquatableHashable 協議,則編輯器會預設為 struct 合成 EquatableHashable 的程式碼,只要其所有的屬性都符合 EquatableHashable 協議,不再需要我們去寫一大堆的模板程式碼,減少了我們的程式碼量。[SE-0185]

struct Country: Hashable {
    let name: String
    let capital: String
    
    // 預設實現了 static func ==(lhs: Country, rhs: Country) -> Bool 和  var hashValue: Int
    // 不再需要我們自己寫程式碼
}
複製程式碼

6 列舉型別如果要符合 EquatableHashable 協議,編譯器也會預設合成了 EquatableHashable 的實現,同上。[SE-0185]

7 擴充套件 Key-path 表示式在標準庫中的使用範圍。讓標準庫中所有的索引型別都符合 Hashable 協議,這樣,[Int]String 和所有其它標準集合使用 key-path 下標時,表現都是一樣的。[SE-0188]

let cos = "Cosmin"
let newPath = \String.[cos.startIndex]
let initial = cos[keyPath: newPath]     // C
複製程式碼

8 支援協議中關聯型別的遞迴約束。[SE-0157]

protocol Phone {
    associatedtype Version
    associatedtype SmartPhone: Phone where SmartPhone.Version == Version, SmartPhone.SmartPhone == SmartPhone
}
複製程式碼

9 移除了協議中的 weakunowned[SE-0186]

class Key {}
class Pitch {}

// Swift 4
protocol Tune {
    unowned var key: Key { get set }
    weak var pitch: Pitch? { get set }
}
// Swift 4.1
protocol Tune {
    var key: Key { get set }
    var pitch: Pitch? { get set }
}
複製程式碼

如果在 4.1 中仍然像 4 那樣使用,編輯器會給出警告資訊。

10 棄用標準庫中 Collection 協議的 IndexDistance 關聯型別,統一修改為 Int 型別。[SE-0191]

protocol Collection {
	var count: Int { get }
	func index(_ i: Index, offsetBy n: Int) -> Index
	func index(_ i: Index, offsetBy n: Int, limitedBy limit: Index) -> Index?
	func distance(from start: Index, to end: Index) -> Int
}
複製程式碼

11 新增新的優化模式 -Osize (Optimize for Size) 來專門優化以減少程式碼大小。這一模式可以減少 5% ~ 30% 不等的程式碼大小。不過使用這一模式沒有兼得減小程式碼大小和執行速度。如果對執行效能要求高,-O 模式還是首選。

12 新增 #if canImport(Framework) 來判斷是否可以匯入某個 Framework[SE-0075]

#if canImport(UIKit)
print("UIKit is available if this is printed!")
#endif
複製程式碼

13 新增 targetEnvironment(target) 來判斷目標環境。[SE-0190]

#if targetEnvironment(simulator)
print("Testing in the simulator.")
#endif
複製程式碼

14 重新命名 Sequence.flatMap(_:)Sequence.compactMap(_:),以讓這個操作的含義更清晰和唯一。[SE-0187]

let pets = ["Sclip", nil, "Nori", nil]
pets.flatMap {$0}   // Warning: 'flatMap' is deprecated
pets.compactMap {$0}
複製程式碼

15 可以直接使用 UnsafeMutableBufferPointer,使用方式與 UnsafeBufferPointer 一樣。[SE-184]

// Swift 4.1
let buffer = UnsafeMutableBufferPointer<Int>.allocate(capacity: 10)
let mutableBuffer = UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(buffer))

// Swift 4
let buffer1 = UnsafeMutableBufferPointer<Int>(start: UnsafeMutablePointer<Int>.allocate(capacity: 10), count: 10)
let mutableBuffer1 = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(mutating: buffer.baseAddress), count: buffer.count)
複製程式碼

總體來說,這次更新變動不大,沒有破壞性更新。包含了核心語言的一些更新,這些更新為後面 Swift 5 的更新做準備。Swift 5 將帶來傳說中的 ABI 的穩定,準備再學一門新語言吧。

掃描關注 知識小集

Swift 4.1 新特性概覽

相關文章