hiccLoghicc log by wccHipo日志

Swift中的模式匹配

Swift 作为一门现代语言,除去安全,快速等特性之外,还有个明显有别于其他语言的特性,就是巨量细致入微的语言特性。类似iOS API,初学者觉得繁杂,无从下手,但是熟悉之后,绝对能少写不少代码。

其中强大的模式匹配绝对让你用的很爽。

主要整理自:pattern-matching-in-swift

迭代器中#

我们经常会在for循环中,使用if判断。

func deleteMarkedMessages() { for message in messages { if message.isMarked { database.delete(message) } } }

上面绝对能工作,你可能会使用函数式的写法。

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数据来说,例如类似的数据结构:

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 }

这个乍看很奇怪,一般只会在switchenum声明中才能看到case。但是实际上,swift中optional值底层是Optional<Wrapped>的枚举enum,而且swift的模式匹配不是只在switch下才能工作。

switch中的optional可选判断#

例如如下的enum,

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的强大的模式匹配下,我们可以写出声明式的代码。

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写起来更简洁。

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 这门语言让人又爱又恨的地方。

从我角度来说,我喜欢这种复杂度,可玩性更高~^-^~。

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=s4vceg07lp5g