为什么 `Mirror.child.label` 字段没有正确处理自引用 类?
Why is the `Mirror.child.label` field not handling self-referential classes properly?
我在 Swift 中使用反射将通用对象 (Any) 写入 Swift 中的字典;用于教育和生产目的。
该代码有效,但我的一项测试表明,对于自引用 classes,它无法正常工作。
访问与class类型相同的成员字段时,Mirror.child.label字段returns取值some
作为字典键,在访问之前成员的变量。
一个很好的例子:
class Node{
var next: Node!
var val: Int = 0
init(_ val: Int){
self.val = val
}
}
let node = Node(4)
node.next = Node(5)
node.next.next = Node(6)
print("node.toDictionary: " , toDictionary(obj: node))
输出为:
node.toDictionary: ["val": 4, "next": ["some": ["next": ["some": ["val": 6, "next": [:]]], "val": 5]]]
不用说了,预期的输出是:
["next": ["next": ["val": 6],"val": 5],"val": 4]
产生此错误的 toDictionary
的最小代码是:
func toDictionary(obj: Any) -> [String:Any] {
var dict = [String:Any]()
let otherObj = Mirror(reflecting: obj)
for child in otherObj.children {
if let key = child.label {
if child.value is String || child.value is Character || child.value is Bool
|| child.value is Int || child.value is Int8 || child.value is Int16 || child.value is Int32 || child.value is Int64
|| child.value is UInt || child.value is UInt8 || child.value is UInt16 || child.value is UInt32 || child.value is UInt64
|| child.value is Float || child.value is Float32 || child.value is Float64 || child.value is Double
{
dict[key] = child.value
}
else if child.value is [String] || child.value is [Character] || child.value is [Bool]
|| child.value is [Int] || child.value is [Int8] || child.value is [Int16] || child.value is [Int32] || child.value is [Int64]
|| child.value is [UInt] || child.value is [UInt8] || child.value is [UInt16] || child.value is [UInt32] || child.value is [UInt64]
|| child.value is [Float] || child.value is [Float32] || child.value is [Float64] || child.value is [Double]{
let array = child.value as! [Any]
dict[key] = array
}else if child.value is [Any]{
let array = child.value as! [Any]
var arr:[Any] = []
for any in array{
arr.append(toDictionary(obj: any))
}
dict[key] = arr
}
else{
dict[key] = toDictionary(obj: child.value)
}
}
}
return dict
}
为什么 Mirror.child.label
字段没有正确处理自引用 classes?
原因是 var: next: Node!
是一个隐式展开的可选值,在许多情况下它们被视为常规可选值,例如
所以node.next
的值实际上是Optional<Node>.some(nextNode)
,这导致创建的字典中的["some": ...]
。
该效果与 属性 的自引用无关,如下例所示:
class Bar {
let value = 1
}
class Foo {
var bar: Bar! = Bar()
}
let foo = Foo()
print(toDictionary(obj: foo))
// ["bar": ["some": ["value": 1]]]
我在 Swift 中使用反射将通用对象 (Any) 写入 Swift 中的字典;用于教育和生产目的。
该代码有效,但我的一项测试表明,对于自引用 classes,它无法正常工作。
访问与class类型相同的成员字段时,Mirror.child.label字段returns取值some
作为字典键,在访问之前成员的变量。
一个很好的例子:
class Node{
var next: Node!
var val: Int = 0
init(_ val: Int){
self.val = val
}
}
let node = Node(4)
node.next = Node(5)
node.next.next = Node(6)
print("node.toDictionary: " , toDictionary(obj: node))
输出为:
node.toDictionary: ["val": 4, "next": ["some": ["next": ["some": ["val": 6, "next": [:]]], "val": 5]]]
不用说了,预期的输出是:
["next": ["next": ["val": 6],"val": 5],"val": 4]
产生此错误的 toDictionary
的最小代码是:
func toDictionary(obj: Any) -> [String:Any] {
var dict = [String:Any]()
let otherObj = Mirror(reflecting: obj)
for child in otherObj.children {
if let key = child.label {
if child.value is String || child.value is Character || child.value is Bool
|| child.value is Int || child.value is Int8 || child.value is Int16 || child.value is Int32 || child.value is Int64
|| child.value is UInt || child.value is UInt8 || child.value is UInt16 || child.value is UInt32 || child.value is UInt64
|| child.value is Float || child.value is Float32 || child.value is Float64 || child.value is Double
{
dict[key] = child.value
}
else if child.value is [String] || child.value is [Character] || child.value is [Bool]
|| child.value is [Int] || child.value is [Int8] || child.value is [Int16] || child.value is [Int32] || child.value is [Int64]
|| child.value is [UInt] || child.value is [UInt8] || child.value is [UInt16] || child.value is [UInt32] || child.value is [UInt64]
|| child.value is [Float] || child.value is [Float32] || child.value is [Float64] || child.value is [Double]{
let array = child.value as! [Any]
dict[key] = array
}else if child.value is [Any]{
let array = child.value as! [Any]
var arr:[Any] = []
for any in array{
arr.append(toDictionary(obj: any))
}
dict[key] = arr
}
else{
dict[key] = toDictionary(obj: child.value)
}
}
}
return dict
}
为什么 Mirror.child.label
字段没有正确处理自引用 classes?
原因是 var: next: Node!
是一个隐式展开的可选值,在许多情况下它们被视为常规可选值,例如
所以node.next
的值实际上是Optional<Node>.some(nextNode)
,这导致创建的字典中的["some": ...]
。
该效果与 属性 的自引用无关,如下例所示:
class Bar {
let value = 1
}
class Foo {
var bar: Bar! = Bar()
}
let foo = Foo()
print(toDictionary(obj: foo))
// ["bar": ["some": ["value": 1]]]