Swift 作为一门现代语言,除去安全,快速等特性之外,还有个明显有别于其他语言的特性,就是巨量细致入微的语言特性。类似iOS API,初学者觉得繁杂,无从下手,但是熟悉之后,绝对能少写不少代码。
其中强大的模式匹配绝对让你用的很爽。
主要整理自:pattern-matching-in-swift
迭代器中
我们经常会在for
循环中,使用if
判断。
func deleteMarkedMessages() {
for message in messages {
if message.isMarked {
database.delete(message)
}
}
}
上面绝对能工作,你可能会使用函数式的写法。
代码语言:javascript复制func deleteMarkedMessages() {
messages.filter { $0.isMarked }
.forEach(database.delete)
}
上述可能需要团队熟悉函数式编程,而且整个数组会经过两遍处理。
而Swift中where
判断,则是此类情况的绝佳解决方案。
func deleteMarkedMessages() {
for message in messages where message.isMarked {
database.delete(message)
}
}
而对于Swift Optional数据来说,例如类似的数据结构:
代码语言:javascript复制struct Match {
var startDate: Date
var players: [Player?]
}
假设我们需要循环players
,且需要排除nil
的值,我们可以使用传统的if
判断,或者使用函数式的,compactMap
函数。但是在Swift中有个更高阶的方法,使用for case let
语法。
func makePlayerListView(for players: [Player?]) -> UIView {
let view = PlayerListView()
for case let player? in players {
view.addEntryForPlayer(named: player.name,
image: player.image)
}
return view
}
这个乍看很奇怪,一般只会在switch
和enum
声明中才能看到case。但是实际上,swift中optional值底层是Optional<Wrapped>
的枚举enum
,而且swift的模式匹配不是只在switch
下才能工作。
switch中的optional可选判断
例如如下的enum,
代码语言:javascript复制enum LoadingState {
case loading
case failed(Error)
}
在无状态的情况下,我们使用可选optional的LoadingState?
,在switch
匹配中,我们同样可以将?
使用在case的情况,以此来匹配有值的情况。
extension ContentViewController: ViewModelDelegate {
func viewModel(_ viewModel: ViewModel,
loadingStateDidChangeTo state: LoadingState?) {
switch state {
case nil:
removeLoadingSpinner()
removeErrorView()
renderContent()
case .loading?:
removeErrorView()
showLoadingSpinner()
case .failed(let error)?:
removeLoadingSpinner()
showErrorView(for: error)
}
}
}
声明式的错误处理
错误处理,特别是http错误处理通常挺复杂的,一大堆if else代码。而在swift的强大的模式匹配下,我们可以写出声明式的代码。
代码语言:javascript复制func handle(_ error: Error) {
switch error {
// Matching against a group of offline-related errors:
case URLError.notConnectedToInternet,
URLError.networkConnectionLost,
URLError.cannotLoadFromNetwork:
showOfflineView()
// Matching against a specific error:
case let error as HTTPError where error == .unauthorized:
logOut()
// Matching against our networking error type:
case is HTTPError:
showNetworkErrorView()
// Fallback for other kinds of errors:
default:
showGenericErrorView(for: error)
}
}
模式匹配底层逻辑,以及自定义模式匹配
Swift中模式匹配部分依赖变量相关语法(例如case let), 这里值和模式匹配的真正逻辑并没有到编译那一步,甚至也不是语言语法,类似很多貌似“底层”的特性其实是在标准库中通过常规的Swift 代码来实现。
具体,Swift使用重载~=
运算符号来实现模式匹配——这也就就给了我们自定义模式匹配的方法。
类似上面的判断错误,使用~=
运算符号重载
func ~=<E: Error & Equatable>(rhs: E, lhs: Error) -> Bool {
return (lhs as? E) == rhs
}
这样,上述的switch case写起来更简洁。
代码语言:javascript复制func handle(_ error: Error) {
switch error {
case URLError.notConnectedToInternet,
URLError.networkConnectionLost,
URLError.cannotLoadFromNetwork:
showOfflineView()
case HTTPError.unauthorized:
logOut()
case is HTTPError:
showNetworkErrorView()
default:
showGenericErrorView(for: error)
}
}
swift 模式匹配很强大,无疑会减少不少代码,不过里面高级的玩法确实看起来容易费解,这点可能就是swift 这门语言让人又爱又恨的地方。
从我角度来说,我喜欢这种复杂度,可玩性更高~^-^~。