Swift 4.1 中引入了一個“新”函式 compactMap
,在這裡給大家介紹一個遷移的小技巧。
compactMap
的由來
在開始之前,先簡單介紹一下 compactMap
的由來,我們都知道之前 flatMap
有兩個過載版本,第一個是用來 flat 集合的:
let arr = [[1, 2, 3], [4, 5]]
let newArr = arr.flatMap { $0 }
// newArr 的值為 [1, 2, 3, 4, 5]
複製程式碼
第二個是用來 flat 可選值的:
let arr = [1, 2, 3, nil, nil, 4, 5]
let newArr = arr.flatMap { $0 }
// newArr 的值為 [1, 2, 3, 4, 5]
複製程式碼
這兩個版本雖然都是用來降維的,但第二個版本除了 flat 之外其實還有 filter 的作用,在使用時容易產生歧義,所以社群認為最好把第二個版本重新拆分出來,使用一個新的方法命名,就產生了這個提案 SE-0187。
最初這個提案用了 filterMap
這個名字,但後來經過討論,就決定參考了 Ruby 的 Array::compact
方法,使用 compactMap
這個名字。
遷移小技巧
雖然就 API 層面來說,這只是單純的名字修改,但全域性替換很難實現,而逐個編譯警告處理又太過麻煩,在這裡我想介紹一個更加便捷的遷移方法:構造一個 flatMap
來過載標準庫的實現,然後再借助 Xcode 的重構工具對函式進行重新命名。
方式很直白,唯一的問題在於如何過載,首先看一下 flatMap
的宣告,flatMap
是宣告在 Sequence
型別裡的:
// stdlib/public/core/SequenceAlgorithms.swift
extension Sequence {
@inline(__always)
@available(swift, deprecated: 4.1, renamed: "compactMap(_:)",
message: "Please use compactMap(_:) for the case where closure returns an optional value")
public func flatMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
return try _compactMap(transform)
}
}
複製程式碼
再參考我之前的博文【譯】Swift 泛型宣言:
...
參考 Swift 官方文件 Protocols 小節裡的最後一段:
“If a conforming type satisfies the requirements for multiple constrained extensions that provide implementations for the same method or property, Swift will use the implementation corresponding to the most specialized constraints.”
約束越多的 conformance 優先順序越高
...
綜上可得,由於 flatMap
是一個宣告在 Sequence
裡的泛型函式,所以我們可以在一個約束更多的 extension 裡宣告一個 flatMap
進行過載,例如繼承自 Sequence
的 Collection
:
protocol Collection: Sequence {}
複製程式碼
又或者是某個 Nested Type,例如 Array
,鑑於我們專案裡基本上都是使用 Array.flatMap
,所以直接過載即可:
extension Array {
func flatMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
return try compactMap(transform)
}
}
複製程式碼
接著右鍵 -> Refactor -> Rename,把 flatMap
改成 compactMap
:
最後把我們剛剛過載的方法刪掉,遷移完成了!!!
覺得文章還不錯的話可以關注一下我的部落格