jjzjj

Swift枚举详解

你duck不必呀 2023-09-17 原文

和结构体一样,swift中的枚举也是值类型。除了定义一个或多个case成员,还可以做以下事情:

  • 可以定义方法,计算属性,下标
  • 可以通过mutating定义可变方法
  • 可以扩展,遵守协议,支持范型

和结构体的唯一区别就是枚举不能定义存储属性
1. 枚举的语法:

enum TextAlignment{
    case left
    case right
    case center
}
//或者 写在一行
enum TextAlignment{
    case left, right, center
}

2. 遵守协议&遍历枚举值

例如:系统自带的CaseIterable协议,allCases属性列出所有的枚举成员,也可以通过手动添加计算属性或方法达到同样的目的,对于没有关联值的枚举,编译器会自动为其实现allCases(成员顺序和声明时候一致)

extension TextAlignment:CaseIterable{}
TextAlignment.allCases // left right center
// 或者 手动添加计算属性
extension TextAlignment {
    static var allCases: [TextAlignment] {
            [.left,.right,.center]
    }
}

3. 原始值、隐式赋值

C或Objective-C中的枚举,是可以通过0 1 2 3...来初始化和使用。Swift枚举默认不会为每个成员分配0 1 2 3...也不能通过整数初始化枚举。要想获得这种特性,必须添加原始值。语法如下

enum TextAlignment:Int {}

原始值可以是字符串、字符或任何整数或浮点数类型。但是每个值对应的原始值必须是唯一的。

enum TextAlignment:String {
    case left = "0"
    case right = "1"
    case center = "2"
}

通过rawValue创建的枚举值是可选的?

var aligent = TextAlignment(rawValue:"1") 
// Optional(TextAlignment. right)

RawRepresentable协议

实现 RawRepresentable 协议的类型会获得两个新的 API:一个 rawValue 属性和一个可失败的初始化方法 (init?(rawValue:)),编译器自动为具有原始值的枚举实现这个协议。

编译器会通过递增前一个成员的原始值来给下一个枚举成员赋值。

enum TextAlignment:Int{
    case left
    case right
    case center
}
// 系统会自动分配 0 ,1,2给成员
var alignment : TextAlignment = .center 
alignment.rawValue // 2

原始值是String的时候,默认情况下系统用 case 值来初始化枚举原始值:

enum TextAlignment:String{
    case left
    case right
    case center
}
var alignment: TextAlignment = .left
print(alignment.rawValue) // "left"

4. 枚举关联值

原始值与关联值不同,枚举成员一旦设置了原始值使用中就不能在变,关联可以根据使用变化。

一个确定枚举值加上其关联值可以使的该枚举类型有更多的成员,Swift可以枚举成员关联任意类型的值

enum TextAlignment{
    case left(String)
    case right(String)
    case center(CGFloat,CGFloat)
}

5. 枚举支持范型&关联值

范型是Swift最强大的功能之一,Swift标准库的大部分都是用通用代码构建的。它能使您编写灵活、可重用的函数和类型,这些函数和类型可以根据您定义的要求适用于任何类型,避免重复的代码。

关于枚举的范型,看如下的例子:
定义范型枚举Result

// 定义范型枚举Result
enum Result<Success, Failure>{
    case success(Success)
    case failure(Failure)
}

使用Result

struct LError:Error{
    var message = "错误信息"
}
// 网络请求结果
var result:Result<Dictionary, LError > = .success(["code":"200"])
// 失败时 
result = .failed(LError(message: "404"))
//通过switch匹配结果
switch result{
case.success(let data):
    print(data)
case.failed(let error):
    let ler = error as! LError
    print(ler.message)
}

实际上,范型枚举都是和关联值结合使用的。Swift自带的Result类型也是这种形式的枚举。
假如我们要从本地磁盘读取文件,这个过程可能会失败,可以设定读取结果可选?这样以来,读取失败就会返回nil,但是我们不知道失败的原因,Result就可以表达那些需要提供详细信息的错误。如果我们不关注错误原因,直接忽略failed后面的关联值即可(下文有讲述如何忽略关联值)。

  1. 枚举使用就简单介绍到这里,接下来重点看枚举的使用和模式匹配。

Swift 要求枚举必须考虑到每一种情况,最常用方法就是使用 switch 语句

普通枚举,没有关联值的情况

var aligent:TextAlignment = .left
// 模式匹配 ,所有情况 left, right,center
switch aligent{
case .left:
    break
case .right:
    break
case .center:
    break
}

通配符 _ ,下划线匹配任意值并忽略这个值,等同default

// default 忽略
switch aligent {
case .left:
    break
default:
    break
}
// 通配符 _ 
switch aligent {
case .left:
    break
case _ :
    break
}

匹配多个值的情况,多个枚举值可以写在一行,也可以使用通配符_,default。

// 匹配多个的情况
switch aligent {
case .left,.right:
    break
case .center:
    break
}

除了switch之外,也可以用if 和if case 匹配

if aligent == .left{
  // do ...    
}

if case .left = aligent {
     // do ... 
}

值绑定模式,像处理可选值一样,可以把枚举的关联值绑定到一个let or var 声明的变量上,带关联值的枚举如下:

var aligent1:TextAlignment = .left("left")
var aligent2:TextAlignment = .center(0, 0)
// 值绑定模式
switch aligent1 {
case .left(let string): //绑定到变量 string 
    break
case .right(let string):
    break
case .center(let x, let y): //绑定到变量x,y 
    break
}
// let写在前面也是可以的
switch aligent1 {
case let.center(x, y):
    break
case let.right(str):
    break
case _:
    break
}

如果想忽略关联值,可以按照编译器提示用下划线_忽略,或者不写后面的括号()

// 用下划线_忽略
switch aligent1 {
case .left(_):
    break
case .right(_):
    break
case .center(_,_):
    break
}
// 不写后面的括号()
switch aligent1 {
case .left:
    break
case .right:
    break
case .center:
    break
}

关联值有单个or多个,匹配确定的关联值。

// 匹配确定的关联值  center(x,y)中的x=0,y=0的情况
switch aligent1 {
case .center(0,_):
    break
case _:
    break
}
// 只匹配确定的关联值  center(x,y)中的x=0的情况
switch aligent1 {
case .center(0,_):
    break
case _:
    break
}

有关Swift枚举详解的更多相关文章

  1. ruby - 引用具有指定索引的枚举器值 - 2

    假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum

  2. ruby-on-rails - Ruby on Rails,使用同名枚举的问题 - 2

    以下是我认为的一些下拉列表:'form-control')%>和'form-control')%>这是我的application_helper.rbdefget_advance_bookingret=[{:require_booking=>'No'},{:require_booking=>'Yes'}]enddefget_instant_bookingret=[{:instant_booking=>'No'},{:instant_booking=>'Yes'}]end但现在的问题是,在我的模型product.rb中,我无法设置具有相同名称的枚举:classProduct我收到的错误是您

  3. ruby-on-rails - i18n 与枚举 gem - 2

    我正在使用Enumerizegemhttps://github.com/brainspec/enumerize/它允许我以简单的形式使用漂亮的选择。并且此选择中的所有选项均已翻译。en:enumerize:user:sex:male:'Man'female:'Woman'所以,在我的表单中,我选择了变体“男人”和“女人”。当我用“男人”值保存记录时,我得到了“男性”值的性别属性。现在我想在显示页面上将该值显示为“Man”,但是=@user.sex输出为'male'而不是'Man' 最佳答案 我可能会使用.text方法(您可以通过使用

  4. ruby-on-rails - Ruby:如何在 fixture 中配置枚举? - 2

    给定这个类:classUser我想创建一个如下所示的fixture:testuser1:id:1username:sampermission::permission_staff我尝试了多种语法变体,但没有找到有效的方法。结果user.permission为nil或0。我知道enum是最近添加的。这可以做到吗? 最佳答案 根据enumdocs你可以像这样通过类引用可枚举的:User.permissions[:permission_staff]工厂只是ruby​​代码——所以他们应该能够以相同的方式访问值testuser1:id:1us

  5. arrays - Ruby 可枚举 - 查找最多 n 次匹配元素 - 2

    我有以下数组:arr=[1,3,2,5,2,4,2,2,4,4,2,2,4,2,1,5]我想要一个包含前三个奇数元素的数组。我知道我可以做到:arr.select(&:odd?).take(3)但我想避免遍历整个数组,而是在找到第三个匹配项后返回。我想出了以下解决方案,我相信它可以满足我的要求:my_arr.each_with_object([])do|el,memo|memo但是有没有更简单/惯用的方法来做到这一点? 最佳答案 使用lazyenumerator与Enumerable#lazy:arr.lazy.select(&:o

  6. ruby - 使用 Ransack 搜索枚举字段 - 2

    我有一个表,'jobs'和一个枚举字段'status'。status具有以下枚举集:enumstatus:[:draft,:active,:archived]使用ransack,我如何过滤表,比如说,所有事件记录? 最佳答案 你可以像这样在模型中声明自己的掠夺者:ransacker:status,formatter:proc{|v|statuses[v]}do|parent|parent.table[:status]end然后您可以使用默认的搜索语法_eq来检查相等性,如下所示:Model.ransack(status_eq:'ac

  7. Ruby 删除可枚举列表中的重复项 - 2

    ruby中有没有一个很好的方法来删除可枚举列表中的重复项(即拒绝等) 最佳答案 对于数组你可以使用uniq()方法a=["a","a","b","b","c"]a.uniq#=>["a","b","c"]所以如果你只是(1..10).to_a.uniq或%w{antbatcatant}.to_a.uniq因为无论如何,几乎所有您实现的方法都将作为Array类返回。 关于Ruby删除可枚举列表中的重复项,我们在StackOverflow上找到一个类似的问题: h

  8. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  9. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  10. ruby - 跨线程共享枚举器 - 2

    我想从不同线程调用一个公共(public)枚举器。当我执行以下操作时,enum=(0..1000).to_enumt1=Thread.newdopenum.nextsleep(1)endt2=Thread.newdopenum.nextsleep(1)endt1.joint2.join它引发了一个错误:Fibercalledacrossthreads.当enum在从t1调用一次后从t2调用时。为什么Ruby设计为不允许跨线程调用枚举器(或纤程),以及是否有其他方法可以提供类似的功能?我猜测枚举器/纤程上的操作的原子性在这里是相关的,但我不完全确定。如果这是问题所在,那么在使用时独占锁定

随机推荐