Swift enum枚举类型使用详解

   2023-02-07 学习力0
核心提示:目录前言AFErrorResultOptional最后举一个enum的典型例子总结前言我一直在思考如何去讲解Swift中的枚举类型,它是如此让人熟悉,不免就让你跟着编程经验走,列举几个状态就感觉万事大吉了。它是如此让人陌生,当你深刻的理解其广泛的用途后,你不得不叹服苹果

前言

我一直在思考如何去讲解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非常密切。

以上就是Swift enum枚举类型使用详解的详细内容,更多关于Swift enum枚举类型的资料请关注其它相关文章!

原文地址:https://juejin.cn/post/6972327170181955592
 
标签: Swift enum 枚举类型
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • swift 命令行工具初探
    亲爱的同学们好,今天我们要介绍这么一个东西。相信有过解释型语言(PHP,Ruby,等)使用经验的同学会更加熟悉,就是 Swift 也为我们提供了命令行运行工具,俗称 REPL。好了,我们进入正题,在安装好 Swift 开发环境的机器上,打开命令行,输入 swift 命令,就进
    03-16
  • [Swift]冒泡排序 | Bubble sort
    [Swift]冒泡排序 | Bubble sort
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)➤GitHub地址:https://github.com/strengthen/LeetCode➤原文
    03-08
  • [Swift] 自定义在 SwiftUI 中实现的可搜索的外观
    [Swift] 自定义在 SwiftUI 中实现的可搜索的外
    首先我找遍了,似乎找不到任何信息......(我遇到了许多根本不起作用的事情......)终于在详细的英文文献中找到了,我会保留下来,希望以后有机会。关于 SwiftUI 中的可搜索searchable是iOS15新增的易于实现的搜索字段。关于这种情况有一个参数placement,您
    03-08
  • [Swift] 检测 SwiftUI ScrollView 中的偏移量
    [Swift] 检测 SwiftUI ScrollView 中的偏移量
    首先你可以用ScrollViewReader做一些可以从iOS14使用的事情。但是,我不能做我想做的事情,所以我想我还能做些什么。再次,可重复使用我尝试过了。执行我将首先发布实现的图像。 (Swift Playgrounds 演示)您可以像这样根据滚动获取坐标。让我们看看实现。1.
    03-08
  • 什么是 LLVM?Swift, Rust, Clang 等语言背后的原力
    什么是 LLVM?Swift, Rust, Clang 等语言背后的
    点击上方“iOS开发”,选择“置顶公众号”关键时刻,第一时间送达!创造新的语言,变着花样的提升现有语言的能力,这在整个编程界正风行。Mozilla 的 Rust、Apple 的 Swift、Jetbrains 的 Kotlin,以及许多其它的语言都给开发者在速度、安全性、便利性、可移
    02-09
  • Swift3.0 UICollectionView 删除,拖动
    Swift3.0 UICollectionView 删除,拖动
    UICollectionView实现了一下常见的新闻分类.  附有效果图 近期一直在深入学习swift,实现了CollectionView item的头东与删除,用的都是系统的一些函数方法,看起来比较直观. 第一步:class HotViewController: UIViewController,UICollectionViewDelegate,UICo
    02-09
  • swift -懒加载创建view
     // 只有外界访问到headerView的时候才会去执行闭包, 然后将闭包的返回值赋值给headerView    // 注意: 一定要记住闭包后面需要写上(), 代表执行闭包    //懒加载创建UIView    lazy var headerView: UIView = {        let view = UIView()
    02-09
  • Swift--非常好用的适合局部的代码约束
    // 哪个控件的哪个属性等于(大于、小于)另外一个控件的哪个属性乘以多少再加上多少 eg:let widthContraint = NSLayoutConstraint(item: messageLabel, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLa
    02-09
  • iOS打包framework - Swift完整项目打包Framework,嵌入OC项目使用
    iOS打包framework - Swift完整项目打包Framewor
    场景说明:-之前做的App,使用Swift框架语言,混合编程,内含少部分OC代码。-需要App整体功能打包成静态库,完整移植到另一个App使用,该App使用OC。-所以涉及到一个语言互转的处理,以及一些AppDelegate的代码减除变化。 --------------------------------
    02-09
  • Swift -- 官方文档Swift-Guides的学习笔记
    在经历的一段时间的郁闷之后,我发现感情都是虚伪的,只有代码是真实的(呸)因为看了swift语法之后依然不会用swift,然后我非常作死的跑去看官方文档,就是xcode里自带的help》documentation and API reference其中的swift里的guide这里主要总结一下里面每一
    02-09
点击排行