網站首頁 編程語言 正文
前言
我一直在思考如何去講解Swift中的枚舉類型,它是如此讓人熟悉,不免就讓你跟著編程經驗走,列舉幾個狀態就感覺萬事大吉了。它是如此讓人陌生,當你深刻的理解其廣泛的用途后,你不得不嘆服蘋果在設計enum是多么讓人眼前一亮。
我思來想去,還是從Swift中最有特點的兩個系統枚舉入手:Optional和Result,而著名的第三庫中選擇的話,Alamofire中細化的AFError也是全面理解Swift中enum的很好的范例。
AFError
由于Alamofire中AFError這個文件中注釋加上代碼已經接近900行了,所以我在舉例說明中會精簡很大一部分,通過拋磚引玉的方式讓大家了解Swift中的enum的特點特色。
import Foundation public enum AFError: Error { /// The underlying reason the `.urlRequestValidationFailed` public enum URLRequestValidationFailureReason { /// URLRequest with GET method had body data. case bodyDataInGETRequest(Data) } /// `URLRequest` failed validation. case urlRequestValidationFailed(reason: URLRequestValidationFailureReason) } // MARK: - Error Descriptions extension AFError: LocalizedError { public var errorDescription: String? { switch self { case .explicitlyCancelled: return "Request explicitly cancelled." case let .invalidURL(url): return "URL is not valid: \(url)" case let .parameterEncodingFailed(reason): return reason.localizedDescription case let .parameterEncoderFailed(reason): return reason.localizedDescription case let .multipartEncodingFailed(reason): return reason.localizedDescription case let .requestAdaptationFailed(error): return "Request adaption failed with error: \(error.localizedDescription)" case let .responseValidationFailed(reason): return reason.localizedDescription case let .responseSerializationFailed(reason): return reason.localizedDescription case let .requestRetryFailed(retryError, originalError): return """ Request retry failed with retry error: \(retryError.localizedDescription), \ original error: \(originalError.localizedDescription) """ case .sessionDeinitialized: return """ Session was invalidated without error, so it was likely deinitialized unexpectedly. \ Be sure to retain a reference to your Session for the duration of your requests. """ case let .sessionInvalidated(error): return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")" #if !(os(Linux) || os(Windows)) case let .serverTrustEvaluationFailed(reason): return "Server trust evaluation failed due to reason: \(reason.localizedDescription)" #endif case let .urlRequestValidationFailed(reason): return "URLRequest validation failed due to reason: \(reason.localizedDescription)" case let .createUploadableFailed(error): return "Uploadable creation failed with error: \(error.localizedDescription)" case let .createURLRequestFailed(error): return "URLRequest creation failed with error: \(error.localizedDescription)" case let .downloadedFileMoveFailed(error, source, destination): return "Moving downloaded file from: \(source) to: \(destination) failed with error: \(error.localizedDescription)" case let .sessionTaskFailed(error): return "URLSessionTask failed with error: \(error.localizedDescription)" } } } extension AFError.URLRequestValidationFailureReason { var localizedDescription: String { switch self { case let .bodyDataInGETRequest(data): return """ Invalid URLRequest: Requests with GET method cannot have body data: \(String(decoding: data, as: UTF8.self)) """ } } }
我們先一點點的分析吧:
- enum是一種特殊的struct!
- enum可以遵守協議,你看開頭一上來就是
public enum AFError: Error
,表示就是這個AFError遵守Error協議,感興趣的可以看看Error到底是什么喔。 - enum是可以帶參數的,我們看這個例子
case urlRequestValidationFailed(reason: URLRequestValidationFailureReason)
。
這個urlRequestValidationFailed帶了一個reason參數,這個參數的類型是URLRequestValidationFailureReason,仔細看URLRequestValidationFailureReason,它其實也是個枚舉,并且帶參數:
public enum URLRequestValidationFailureReason { /// URLRequest with GET method had body data. case bodyDataInGETRequest(Data) }
怎么樣,有點暈了沒?夠酸爽了吧?這還不夠呢,我們接著看:
enum不僅能帶參數、遵守協議,還能寫分類,并擴展只讀計算屬性與函數,extension AFError: LocalizedError
中就是遵守LocalizedError協議,并實現LocalizedError協議中的屬性var errorDescription: String?
。
我們看看這個只讀計算屬性的里面其中的一個實現:
case let .urlRequestValidationFailed(reason): return "URLRequest validation failed due to reason: \(reason.localizedDescription)"
這個case let的意思是:如果是urlRequestValidationFailed這種情況,獲取這個枚舉中的reason參數,并且進行字符串表達,除了上面這種表達,經常的書寫方式還有下面幾種:
/// 將let放在取枚舉值的地方,我個人比較喜歡這種寫法 case .urlRequestValidationFailed(let reason): /// 不關心枚舉帶參,用_代替 case .urlRequestValidationFailed(_): /// 直接只顯示枚舉的狀態,省略參數顯示 case .urlRequestValidationFailed:
最后一個extension AFError.URLRequestValidationFailureReason
展示了在內嵌的在AFError中的URLRequestValidationFailureReason類型應該如何編寫分類。
怎么樣?單單看一個Alamofire的AFError就有不少的收獲吧?
Swift中的enum,是我目前學習過的編程語言中功能最強大的enum了。
Result
下面的是官方的Result的源碼,我只寫出了主干功能,大家看了,想想enum又支持什么功能呢?
@frozen public enum Result<Success, Failure> where Failure : Error { /// A success, storing a `Success` value. case success(Success) /// A failure, storing a `Failure` value. case failure(Failure) /// 其他的內容我省略了 }
官方提供的Result枚舉在Swift5之后才正式上線,而Github開源的,我們可以看這個庫Result,它的.gitignore創建于6年前,可以看到Result類型的出現并不是偶然,它的出現更多是因為功能上的訴求。
當前的異步回調中,最常見的情況有兩種:回調成功抑或回調失敗,在早期的Swift回調中,我們經常看見這樣寫:
typealias Callback<Success, SomeError: Error> = (Success?, SomeError?) -> Void
因為我知道回調到底是否成功,所以Success與SomeError都是可選類型,使用Result類型后,我們就可以這么寫了:
typealias ResultCallback<Success, SomeError: Error> = (Result<Success, SomeError>) -> Void
回調的結果,有兩種情況,真實可靠——case success和case failure,直接減少了可選類型的使用,也就精簡了判空的邏輯。
通過Result這個枚舉,我們可以看出Swift中的enum是支持泛型的!!!
如果你一直使用過Alamofire和Kingfisher,你會發現它們里面的回調變遷也是圍繞這個上面兩個閉包的例子進行演化的。
最后來介紹一下Swift中的可選類型。
Optional
下面的是官方的Optional的源碼,我只寫出了主干功能,是不是和Result非常相似呢?
@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral { /// The absence of a value. /// /// In code, the absence of a value is typically written using the `nil` /// literal rather than the explicit `.none` enumeration case. case none /// The presence of a value, stored as `Wrapped`. case some(Wrapped) }
而我經常看見的String?
這個寫法,不過是Optional<String>
的糖語法罷了。
最后舉一個enum的典型例子
enum NumberType: String { case one, two, three, four, five } let s: NumberType = .one print(s.rawValue)
s.rawValue的類型是String,而打印出來的就是字符串"one"。
這種enum NumberType: String
寫法,本質上告訴編譯器枚舉中的rawValue值是String類型,怎么樣,有木有有點暈呢?
大家思考一下下面這種情況:
enum NumberValue: Int { case one = 3, two, three, four, five }
NumberValue.two.rawValue是什么呢?
總結
Swift中的enum,不僅是過去認知中僅僅表示狀態的簡單類型,它有以下這些特性:
- 支持遵守協議
- 支持泛型使用
- 支持編寫擴展
- 支持帶參數
- 支持繼承基礎數據類型,表示枚舉的rawValue的類型
還有其他有點意思的特性可能目前還沒有概括全面,我想表達的是,enum的設計是顛覆性,而靈活運用這些enum特性,才是最難的。
之所以會先講解enum是因為它和我們需要講解的網絡請求封裝庫Moya非常密切。
原文鏈接:https://juejin.cn/post/6972327170181955592
相關推薦
- 2022-12-04 Flutter組件適配方法實現詳解_Android
- 2022-05-25 flutter實現底部導航欄_Android
- 2022-03-26 在ASP.Net?Core應用程序中使用Bootstrap4_實用技巧
- 2022-10-10 python使用pandas讀寫excel文件的方法實例_python
- 2022-07-11 Oracle使用dblink同步數據
- 2022-09-26 Redis?哈希Hash底層數據結構詳解_Redis
- 2023-04-26 C語言二維數組指針的概念及使用_C 語言
- 2023-10-26 在el-table中根據判斷不同值顯示對應文本
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支