Swift 4.2

Runtime Optimizations

Swift 透過ARC 來做記憶體管理,在4.1 以前,Compiler 自動幫我們在該retain、release 的地方加上code(跟Objc 相同)

class X { ... }

func caller() {
    // x 被created 之後,+1 reference count
    let x = X()
    foo(x)
}

func foo(x: X) {
    let y = x.value
    ...

    // 用完x之後,release x
}

而更多的狀況可能會像這樣:

class X { ... }

func caller() {
    // x 被created 之後,+1 reference count
    let x = X()

    // retain x
    bar(x)  // 執行完後,release x
    // retain x
    baz(x)  // 執行完後,release x
    foo(x)  // 執行完後,release x
}

在Swift 4.2,他將被改善成,在同一個function call 裡,create 時+1 reference count,直到所有func 執行完,在release x,如此一來,可以改善runtime performance,code size 也會縮小一點。

class X { ... }

func caller() {
    // x 被created 之後,+1 reference count
    let x = X()

    bar(x) 
    baz(x)
    foo(x)
    // nomore retain, release x once
}

Small String、Reduce code size

除此之外,這次還改善了String 的記憶體用量,以及新的compiler optimization level,來縮小code size,一般來說,我們都會將optimization level 設置成speed 優先,所以compiler 會幫我們inline 一些function call、展開loop 等等的動作,因此,app size 可能會因此而提升。

這次新增的選項,適用於一些非常在乎size 的app 上,Apple 提到,其效能可能只會差5%,但code size 卻可以減少10% – 30%,一切還是得看用途決定。

New Language Features

在開始講新的功能之前,Apple 特別提到Swift Evolution Process:

  1. Pitch to forums.swift.org
  2. Write draft proposal
  3. Implementation
  4. Review period
  5. Decision by core team

也就是說,當你發現需求、經過討論之後,你必須先提案、實作,最後透過core team的決策來決定是否需要merge 成為Swift 的一部分。

或許這樣的process 不免讓人擔心,是不是只是美其名說開源,因此Apple 列出了這次所實作的proposal,當中有將近一半來自社群。

無標題

Collection of Enum Cases

在我們使用Enum 時,很常會遇到需要Travel all cases 的狀況,我們可能會這樣寫:

enum Brand {
    case apple
    case ms
    case facebook

    static var allCases: [Brand] = [.apple, .ms, .facebook]
}

for brand in Brand.allCases {
    print(brand)
}

但如果今天加了一個新的case:

enum Brand {
    case apple
    case ms
    case facebook
    case google // new case

    static var allCases: [Brand] = [.apple, .ms, .facebook]
}


for brand in Brand.allCases {  // 少了google
    print(brand)
}

這樣人工維護的做法,相當的容易產生問題,我們都知道Bug 大多也都是不小心,不是故意的🤣

在Swift 4.2 中,我們只要讓enum confirm CaseIterable protocol 之後,compiler 會自動synthesis allCases,就可以直接用一樣的方式,來使用allCases,而且在你新增case 之後,不會再出錯。👍

enum Brand: CaseIterable {
    case apple
    case ms
    case facebook
    case google
}


for brand in Brand.allCases {
    print(brand)
}

Conditional Conformance

TODO

相關議程:Swift Generics

Synthesized Equatable and Hashable Conformance

在以前,如果你要實作Equatable protocol,你必須要自行加上所有的property 比較,其中不乏一大堆copy & paste:

struct Person {
    let name: String
    let isVIP: Bool
    let hasKids: Bool
}

extension Person: Equatable {
    static func ==(a: Person, b: Person) -> Bool {
        return a.name == b.name &&
               a.isVIP == b.isVIP &&
               a.hasKids == b.hasKids
    }
}

現在只需要這樣,Swift 4.2 幫你自動synthesis 完成了,好處當然也與維護性有關:

struct Person: Equatable {
    let name: String
    let isVIP: Bool
    let hasKids: Bool
}

自動synthesis 在Hashable 上也能使用。

Hashable Enhancements

TODO

Random Number Generation

在Swift 4.0 時,如果我們要跨平台的去產生隨機變數,我們需要:

#if os(iOS) || os(tvOS) || os(watchOS) || os(macOS)
    return Int(arc4random())
#else
    return random() // or Int(rand())
#endif

甚至在某些狀況下,機率還是不均等的:

無標題

在Swift 4.2,有了新的API 來做這件事:

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomFloat = Float.random(in: 0 ..< 1)

let greetings = ["hey", "hi", "hello", "hola"]
print(greetings.randomElement()!)

let randomlyOrderedGreetings = greetings.shuffled()
print(randomlyOrderedGreetings)

透過新的API 我們可以:

  1. 快速的產生隨機變數
  2. 從一個collection 裡面隨機拿出一個element
  3. 洗亂一個collection

此外,可以透過實作RandomNumberGenerator protocol 來實作自己的隨機演算法(灌鉛摋子?🤪),再透過injection 的方式餵給random func 們。

Checking Platform Conditions

在以往處理跨平台code 的共用時,我們會寫如以下的code:

#if os(iOS) || os(tvOS) || os(watchOS)
    import UIKit
    ...
#else
    import AppKit
    ....
#endif

但其實我們並不在乎平台,我們只想確定在現在的環境下,是不是可以import UIKitAppKit,所以現在支援了:

#if canImport(UIKit)
    import UIKit
    ...
#elseif canImport(AppKit)
    import AppKit
    ....
#else
    #error("Unsupported platform")
#endif

另外還有像是環境是否為simulator 的check:

#if os(iOS) || os(tvOS) || os(watchOS) && (cpu(i386) || cpu(X86_64))
    ...
#else
    // FIXME: We need to test this better
    ....
#endif

現在只需要改成:

#if hasTargetEnvironment(simulator)
    ...
#else
    #warning("We need to test this better")
    ....
#endif

Implicitly Unwrapped Optionals

TODO

More

Enforcing Exclusive Access to Memory

TODO