OptionSet vs Enum:Swift 中如何优雅地表达“多选”?

两种"多选"方案的基因差异

维度 OptionSet Enum + Set
底层模型 位图(bitset) 哈希集合(HashSet)
存储大小 固定位宽(UInt8/Int/UInt64) 动态哈希表
性能 位运算 O(1) 哈希操作 O(1)
组合能力 原生支持按位或、补集、交集 需手动写集合运算
可读性 需要位运算知识 更接近自然语言
最大选项数 受位宽限制(UInt8=8,UInt=64) 理论无上限
场景 性能敏感、C 接口、系统 API 业务逻辑、易读的权限/标签

系统框架中的 OptionSet 速览

swift 复制代码
// UIView 自动布局掩码
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

// 动画选项(可以同时指定多个)
UIView.animate(withDuration: 0.3,
               delay: 0,
               options: [.curveEaseInOut, .allowUserInteraction]) { ... }

// JSON 序列化
let data = try JSONSerialization.data(withJSONObject: obj,
                                      options: [.prettyPrinted, .fragmentsAllowed])

自定义 OptionSet:四行代码搞定位运算

最原始写法(不推荐)

swift 复制代码
struct UserRight {
    let rawValue: Int
    static let create = UserRight(rawValue: 1)   // 0001
    static let edit   = UserRight(rawValue: 2)   // 0010
    static let read   = UserRight(rawValue: 4)   // 0100
    static let delete = UserRight(rawValue: 8)   // 1000
}
extension UserRight: OptionSet {}

位左移写法(推荐)

swift 复制代码
struct UserRight: OptionSet {
    let rawValue: Int
    
    static let create = UserRight(rawValue: 1 << 0) // 0001
    static let edit   = UserRight(rawValue: 1 << 1) // 0010
    static let read   = UserRight(rawValue: 1 << 2) // 0100
    static let delete = UserRight(rawValue: 1 << 3) // 1000
    
    // 常用组合
    static let admin: UserRight = [.create, .edit, .read, .delete]
    static let editor: UserRight = [.read, .edit]
}

技巧:用 1 << n2ⁿ 直观,且编译器会检查越界。

与位运算配合

swift 复制代码
var rights: UserRight = [.read, .edit]

let b0 = rights.contains(.read)        // true
let b1 = rights.contains(.delete)      // false

let r = rights.insert(.delete)        // 返回新的 OptionSet
rights.remove(.read)          // 去掉读权限
let b2 = rights == .admin              // false

可读性升级:打印友好

swift 复制代码
extension UserRight: CustomStringConvertible {
    var description: String {
        let map: [(Self, String)] = [
            (.create, "create"),
            (.edit,   "edit"),
            (.read,   "read"),
            (.delete, "delete")
        ]
        return map
            .filter { contains($0.0) }
            .map(\.1)
            .joined(separator: ", ")
    }
}

print(UserRight.admin)   // "create, edit, read, delete"

Enum + Set:当可读性更重要时

定义协议(一次写完,到处复用)

swift 复制代码
protocol OptionProtocol: RawRepresentable, Hashable, CaseIterable {}

枚举声明

swift 复制代码
enum UserRightOption: String, OptionProtocol {
    case create, edit, read, delete
}

// 使用 Set 作为容器
typealias UserRights = Set<UserRightOption>

extension Set where Element == UserRightOption {
    static var editor: UserRights { [.read, .edit] }
    // 这里的好处是可以直接使用Enum的allCases,而如果是optionSet只能手动写全部的case
    static var admin: UserRights  { Set(Element.allCases) }
}

与 OptionSet 互转

swift 复制代码
extension Set where Element: OptionProtocol {
    /// 转成 Int 位图,方便与 C 层交互
    var rawValue: Int {
        var value = 0
        for (index, element) in Element.allCases.enumerated() where contains(element) {
            value |= (1 << index)
        }
        return value
    }
}

let set: UserRights = [.read, .edit]
let bits = set.rawValue   // 6 (0b110)

实战选型建议

场景 推荐 理由
系统 API、CoreGraphics、Metal 标志 OptionSet 必须与 C 位图交互
网络请求参数、数据库字段 OptionSet 体积小、易序列化
业务权限、标签、筛选器 Enum + Set 可读、易扩展、无位宽限制
需要超过 64 个选项 Enum + Set OptionSet 位宽不够

性能基准(macOS, 1M 次操作)

操作 OptionSet (Int) Set
contains 0.05 ms 0.21 ms
insert 0.03 ms 0.35 ms
内存/实例 8 Byte 24 Byte+

PS: 这里只是展示了最基础的num+set用法。enum还有关联值,可以玩更多花活

一句话总结

  • OptionSet = 位图,极致轻量,适合与底层、系统、性能敏感代码打交道。
  • Enum + Set = 哈希集合,可读性高,适合业务层快速迭代。

选型口诀:"对外接口选 OptionSet,对内业务选 Enum+Set,超过 64 个选项直接 Enum。"

相关推荐
大熊猫侯佩1 天前
雪山飞狐之 Swift 6.2 并发秘典:@concurrent 的江湖往事
swiftui·swift·apple
胎粉仔3 天前
Objective-C —— APIs declaration 自定义
ios·objective-c·swift
用户093 天前
Swift Concurrency 中的 Threads 与 Tasks
ios·swiftui·swift
低调小一3 天前
双端 FPS 全景解析:Android 与 iOS 的渲染机制、监控与优化
android·ios·kotlin·swift·fps
用户093 天前
更现代、更安全:Swift Synchronization 框架与 Mutex 锁
ios·面试·swift
大熊猫侯佩6 天前
鹿鼎记豪侠传:Rust 重塑 iOS 江湖(下)
rust·objective-c·swift
大熊猫侯佩6 天前
鹿鼎记豪侠传:Rust 重塑 iOS 江湖(上)
rust·objective-c·swift
用户098 天前
如何避免写垃圾代码:iOS开发篇
ios·swiftui·swift
HarderCoder8 天前
Swift 语法速通:iOS 开发必会的 8 大核心概念(图解+类比)
swift