利用反射 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 源码时学习到的。

猜你喜欢

转载自blog.csdn.net/fuyoufang1/article/details/119407796
今日推荐