利用反射 Mirror 快速的获取/打印枚举值

本文介绍如何在Swift中使用枚举和Mirror简化枚举及其关联值转为字符串的过程,包括实现CustomStringConvertible协议和利用Mirror获取并展示枚举详情。

在 Swift 中,枚举是一等类型,可以给其添加计算属性,实例方法,构造函数,遵循协议,另外,还可以定义枚举来存储任意类型的关联值。这些特性可以让枚举适用于很多场合。

当我们需要打印日志,如何快速的将枚举值与其关联值一起转化成字符串呢?

当然我们可以让 enum 实现 CustomStringConvertible 等协议,然后实现各个 case 到 string 的转化。当 enum 的数量少时,还可以应付,当数量增加时,总得想想如何简化这个过程吧。

Mirror 的简介

Swift 中的反射机制是通过 Mirror 来实现的。我们可以给一个具体的 subject(类、枚举、结构体、数字、闭包等所有的 Any 类型)创建一个 Mirror,然后通过 Mirror 来观察这个 subject

因此,可以通过 Mirror 获取到枚举的关联值的信息,简化将枚举转成字符串的过程,又不丢失枚举的关联值的信息。

Mirror 最简单的构造器为:

public init(reflecting subject: Any)

Mirror 提供了几个属性来告诉我们想查询的信息:

  • displayStyle:类型为 DisplayStyle enum,说明对象的展示类型,如:Struct、Class、Enum 等。
  • children: 类型为 public typealias Child = (label: String?, value: Any) 的数组,其中,label 表示子节点的名称,value 表示子节点的值。
  • subjectType:类型为 Any.Type,说明对象的类型。

利用 Mirror 将枚举快速的转成字符串

假如有如下一个 enum:

enum TestEnum {
    case null // 不关联任何值
    case string(String) // 关联一个 String
    case tuple(String, String) // 关联一个元组 tuple
}

接下里给 TestEnum 添加一个扩展

extension TestEnum {
    public var label: String {
        // 获取到 enum 值对应的 Mirror
        // mirror 的 subjectType 为 TestEnum, displayStyle 为 Optional(Swift.Mirror.DisplayStyle.enum)
        let mirror = Mirror(reflecting: self)        
        
        // associated 为 enum 值的关联值,如果没有关联值则为空。
        // associated 的 label 为关联值的类型, value 为关联值的值
        if let associated = mirror.children.first {           
            // valuesMirror 为枚举值的关联值的 Mirror
            let valuesMirror = Mirror(reflecting: associated.value)

            if !valuesMirror.children.isEmpty {
                // 如果关联值为类、结构体等,children 为其属性
                // 如果关联值为数组、字典,children 为其元素
                // 将 children 转化为字符串
                let parameters = valuesMirror.children.map { ".\($0.value)" }.joined(separator: ",")
                return ".\(associated.label ?? "")(\(parameters))"
            }
            return ".\(associated.label ?? "")(.\(associated.value))"
        }
        return ".\(self)"
    }
}

这样,就可以通过 label 属性获取到枚举值与其关联值转成的字符串:

TestEnum.null.label
TestEnum.string("Hello").label
TestEnum.tuple("Hello", "world").label

参考

  1. 有关 Mirror 的详细介绍,可以参考 Swift 反射 API 及用法
  2. label 方法的实现,为本人在看 Hero 源码时学习到的。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值