天天看點

Swift 4.1 的新特性

  • 蘋果公司在 3.29 正式釋出了正式版的

    Xcode 9.3

    Swift 4.1

    , 讓我們看看

    Swift 4.1

    帶來了哪些新功能和新亮點
  • 測試需要Xcode9.3, 請確定你的Xcode是最新的9.3版本
  • Swift 4.1

    Swift 4.0

    是源代碼相容的,是以如果你已經使用

    Xcode

    中的

    Swift Migrator

    将你的項目遷移到

    Swift 4.0

    ,那麼新特性不會破壞你的代碼
  • 下面在

    Xcode9.3

    下建立一個

    Playground

    工程, 測試我們的代碼

條件一緻性(Conditional Conformance)

  • 條件一緻性使類型參數滿足特定條件的泛型類型的協定一緻性 [ SE-0143 ]
  • 在Swift 4中,如果數組、字典或者可選類型的元素類型遵循

    Equatable

    ,你是可以在數組之間、字典之間和可選類型之間進行比較的, 如下示例:
// Int類型的數組
let arr1 = [, , ]
let arr2 = [, , ]
print(arr1 == arr2)

// 比較value值為Int的字典
let dic1 = ["age": , "score": ]
let dic2 = ["age": , "score": ]
print(dic1 == dic2)

// 比較Int?
let age1 = dic1["age"]
let age2 = dic2["age"]
print(age1 == age2)

/// 以上輸出結果都是: true
複制代碼
           

這裡如果我們把

Int

都換成

Int?

類型, 在

Swift4.0

中是不能編譯通過的, 如下:

// Int類型的數組
let arr1: [Int?] = [, , ]
let arr2: [Int?] = [, , ]
print(arr1 == arr2)

// 比較value值為Int的字典
let dic1: [String: Int?] = ["age": , "score": ]
let dic2: [String: Int?] = ["age": , "score": ]
print(dic1 == dic2)

// 比較Int?
let age1 = dic1["age"]
let age2 = dic2["age"]
print(age1 == age2)
複制代碼
           
  • 在這些執行個體中, 我們用

    ==

    測試相等性, 在Swift4.0中,

    Int

    類型遵循

    Equatable

    協定, 可以比較, 但是

    Int?

    類型卻沒有遵循

    Equatable

    協定
  • 但是在Swift4.1中, 完美的解決了這個問題, 上述代碼可比那已認證, 且都輸出:

    true

  • Swift 4.0

    [Set<Int>]

    之間可以直接對比,但是

    [[Int]]

    不能。現在

    Swift 4.1

    中,

    [[Int]]

    也能直接對比
  • 總的來說,

    Swift 4.1

    Array

    Dictionary

    Optional

    ,隻要他們的元素都遵循了

    Equatable

    Hashable

    ,那麼他們也遵循

    Equatable

    Hashable

合成

Equatable

Hashable

  • 如果對象相等,則這兩個對象的

    hash

    值一定相等
  • 如果兩個對象

    hash

    值相等,這兩個對象不一定相等。
  • Swift

    Hashable

    一定是

    Equatable

    ,因為前者繼承了後者。 在

    Swift 4

    中,若遵循

    Equatable

    協定的時候,我們必須實作

    Equatable

    協定的

    ==

    方法,

    Equatable

    協定如下:
public protocol Equatable {

    /// Returns a Boolean value indicating whether two values are equal.
    ///
    /// Equality is the inverse of inequality. For any values `a` and `b`,
    /// `a == b` implies that `a != b` is `false`.
    ///
    /// - Parameters:
    ///   - lhs: A value to compare.
    ///   - rhs: Another value to compare.
    public static func == (lhs: Self, rhs: Self) -> Bool
}
複制代碼
           

在Swift4.0中, 必須實作

Equatable

協定的方法

struct Name: Equatable {
    var name1 = "name1"
    var name2 = "name2"
    
    static func == (lhs: Name, rhs: Name) -> Bool {
        return lhs.name1 == rhs.name1 &&
            lhs.name2 == rhs.name2
    }
}
複制代碼
           

Swift 4.1

,隻需要加上

Equatable

即可, 不需要實作任何協定方法

struct Name: Equatable {
    var name1 = "name1"
    var name2 = "name2"
    
}
複制代碼
           

JSON

編碼時支援

Camel Case

Snake Case

之間的轉換

  • Swift 4.0

    引入了

    Codable

    ,但是有個麻煩的問題:如果

    JSON

    資料的

    key

    命名格式是

    snake_case

    的話,我們必須建立自己的

    CodingKeys

    來告訴蘋果怎麼轉換。在

    Swift 4.0

  • 但是在

    Swift 4.1

    中,蘋果給

    JSONDecoder

    引入了一個屬性

    keyDecodingStrategy

    ;對應的

    JSONEncoder

    引入了一個屬性

    keyEncodingStrategy

    。這樣我們就不需要設定定義

    CodingKeys

    了。隻需要在

    decoding

    的時候把

    keyDecodingStrategy

    設定為

    .convertFromSnakeCase

    ;在

    encoding

    的時候把

    keyEncodingStrategy

    設定為

    .convertToSnakeCase

  • 下面是分别針對數組/字典/集合的解析形式
struct Student: Codable, Hashable {
  let firstName: String
  let averageGrade: Int
}

let cosmin = Student(firstName: "Cosmin", averageGrade: )
let george = Student(firstName: "George", averageGrade: )
let encoder = JSONEncoder()

// Encode an Array of students
let students = [cosmin, george]
do {
  try encoder.encode(students)
} catch {
  print("Failed encoding students array: \(error)")
}

// Encode a Dictionary with student values
let studentsDictionary = ["Cosmin": cosmin, "George": george]
do {
  try encoder.encode(studentsDictionary)
} catch {
  print("Failed encoding students dictionary: \(error)")
}

// Encode a Set of students
let studentsSet: Set = [cosmin, george]
do {
  try encoder.encode(studentsSet)
} catch {
  print("Failed encoding students set: \(error)")
}

// Encode an Optional Student
let optionalStudent: Student? = cosmin
do {
  try encoder.encode(optionalStudent)
} catch {
  print("Failed encoding optional student: \(error)")
}
複制代碼
           

Hashable Index Types

(哈希化索引)

擴充

Key-path

表達式在标準庫中的使用範圍。讓标準庫中所有的索引類型都符合

Hashable

協定,這樣,

[Int]

String

和所有其它标準集合使用

key-path

下标時,表現都是一樣的

let swiftString2 = "one two three"
let charact1 = \String.[swiftString2.startIndex]
print(swiftString2[keyPath: charact1])

let arr = [, , , ]
let value2 = \[Int].[]
print(arr[keyPath: value2])

//輸出結果:
o

複制代碼
           

compactMap

的用法

Swift 4.0

中,我們經常使用

flatMap

來過濾

nil

,也可以進行降維操作, 詳情可參考Swift函數式程式設計之進階用法

let arr = [1, 2, nil, 3, 4, nil]
let arr1 = arr.flatMap({ $0 })
print(arr1)

//這樣使用會有類似的警告
'flatMap' is deprecated: Please use compactMap(_:) for the case where closure returns an optional value 
Use 'compactMap(_:)' instead

//Swift4.1中的用法
let arr = [1, 2, nil, 3, 4, nil]
let arr2 = arr.compactMap({ $0 })
print(arr2)
複制代碼
           
主要是因為在

Swift4.0

flatMap

有很多重載, 可能會引起歧義, 是以在

Swift4.1

中把

flatMap

重命名為

compactMap

除了協定中的 weak 和 unowned。

  • 當你在

    Tune

    協定中定義了兩個屬性

    key

    pitch

    ,

    pitch

    可能為

    nil

    , 是以你在協定中可以用

    weak

    修飾
  • 但是如果在協定本身中定義的話,

    weak

    unowned

    都沒有實際意義, 是以在

    Swift4.1

    中就已經去掉了這些關鍵字, 并且在協定中使用這些關鍵字将會爆出警告
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 }
}
複制代碼
           

UnsafeMutableBufferPointer

的改變

//Swift4.0
let buffer = UnsafeMutableBufferPointer<Int>(start: UnsafeMutablePointer<Int>.allocate(capacity: ), 
                                             count: )
let mutableBuffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(mutating: buffer.baseAddress), 
                                               count: buffer.count)
                                               
//Swift4.1
let buffer = UnsafeMutableBufferPointer<Int>.allocate(capacity: )
let mutableBuffer = UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(buffer))
複制代碼
           
相對

Swift4.0

的改變,

Swift4.1

這點改變鍵值太微不足道了, 傳說中

Swift5

API

會趨于穩定, 但是估計改變可能也是非常大的, 坐等

Swift5

釋出...

參考文檔

  • What’s New in Swift 4.1?