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:
- Pitch to forums.swift.org
- Write draft proposal
- Implementation
- Review period
- 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 我們可以:
- 快速的產生隨機變數
- 從一個collection 裡面隨機拿出一個element
- 洗亂一個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 UIKit
或 AppKit
,所以現在支援了:
#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
Enforcing Exclusive Access to Memory
TODO