swift codable和mirror

1. Codable

在swift 4中引入codable之后,可以将Cat声明为Codable(或者至少声明为Encodable,记住Codable其实就是Decodable & Encodable),然后使用相关的encoder来进行编码。不过swift标准库中并没有直接将一个对象编码为字典的编码器,我们可以进行一些变通,先将需要处理的类型声明为Coable,然后使用JSONEncoder将其转换为JSON数据,最后再从JSON数据中拿到对应的字典,如下代码所示,

// public typealias Codable = Decodable & Encodable
struct Cat: Codable {
    let name: String
    let age: Int
}

let kitten = Cat.init(name: "kitten", age: 2)
let encoder = JSONEncoder()

do {
    let data = try encoder.encode(kitten)
    let dictionary = try JSONSerialization.jsonObject(with: data, options: [])
    print(dictionary)
} catch {
    print(error)
}

2. Mirror

Mirror可以让我们在运行时一窥某个类型的实例的内容,它也是swift中位数不多的与运行时特性相关的手段。Mirror的最基本的用法如下,


struct Cat {
    let name: String
    let age: Int
}

let kitten = Cat.init(name: "kitten", age: 2)
let mirror = Mirror.init(reflecting: kitten)
for child in mirror.children {
    print("\(child.label!) - \(child.value)")
}

通过访问实例中mirror.children的每一个child,就可以得到所有的存储属性的label和value。以label为字典键,value为字典值,我们就能从任意类型构建出对应的字典。

字典中值的类型

不过逐一,这个child中的值是以Any为类型的,也就是说任意类型都可以在child.value中表达。所以还需要做一些额外的类型保证的工作,这里添加一个DictionaryValue协议,来表示字典能接受的类型,如下代码所示,


protocol DictionaryValue {
    var value: Any { get }
}

extension Int: DictionaryValue {
    var value: Any {
        return self
    }
}

extension Float: DictionaryValue {
    var value: Any {
        return self
    }
}

extension String: DictionaryValue {
    var value: Any {
        return self
    }
}

extension Bool: DictionaryValue {
    var value: Any {
        return self
    }
}

进一步对DictionaryValue协议进行扩展,让满足它的其他类型通过Mirror的方式来构建字典,如下代码所示,

extension DictionaryValue {
    var value: Any {
        let mirror = Mirror(reflecting: self)
        var result = [String: Any]()
        for child in mirror.children {
            // 如果无法获得正确的key,则报错
            guard let key = child.label else {
                fatalError("Invalid key in child:\(child)")
            }
            
            // 如果value无法转换为DictionaryValue,则报错
            if let value = child.value as? DictionaryValue {
                result[key] = value.value
            } else {
                fatalError("Invalid value in child: \(child)")
            }
        }
        return result
    }
}


struct Cat1: DictionaryValue {
    let name: String
    let age: Int
}

let kitten1 = Cat1.init(name: "kitten", age: 2)
print(kitten1.value)

对于潜逃自定义DictionaryValue值的其他类型,字段转换也可以正常工作,如下代码,

struct Wizard: DictionaryValue {
    let name: String
    let cat: Cat1
}

let wizard = Wizard.init(name: "hermione", cat: kitten1)
print(wizard.value)

字典中的嵌套数组和字典

extension Array: DictionaryValue where Element: DictionaryValue {
    var value: Any { return map{ $0.value } }
}

extension Dictionary: DictionaryValue where Value: DictionaryValue {
    var value: Any { return mapValues { $0.value } }
}


/*
// 适配swift 4.1之前的代码
 extension Array: DictionaryValue {
 var value: Any { return map { ($0 as! DictionaryValue).value } }
 }
 extension Dictionary: DictionaryValue {
 var value: Any { return mapValues { ($0 as! DictionaryValue).value } }
 }
 */

struct Gryffindor: DictionaryValue {
    let wizards: [Wizard]
}

let crooks = Cat1.init(name: "Crookshanks", age: 2)
let hermione = Wizard.init(name: "hermione", cat: crooks)


let hedwig = Cat1.init(name: "hedwig", age: 3)
let harry = Wizard.init(name: "Harry", cat: hedwig)
let gryfindor = Gryffindor.init(wizards: [harry, hermione])
print(gryfindor.value)

4. 参考链接

  • https://www.jianshu.com/p/7f43d69ba9d2
  • https://www.jianshu.com/p/eac4a92b44ef
  • https://onevcat.com/2018/03/swift-meta/

你可能感兴趣的:(swift codable和mirror)