如何使用Swift 3中的Mirror

时间:2016-11-02 17:13:22

标签: swift foundation

class A :NSObject {
    let a :Int
    let b :UIColor
}

我不想通过逐个比较所有属性来实现isEqual。如果是这样,当我添加另一个属性时,我应该再次修改isEqual的工具。

在swift中使用Mirror时,我可以方便地打印所有属性。如何使用isEqual方便地实现Mirror方法。

1 个答案:

答案 0 :(得分:2)

除了诊断之外,您不应该使用运行时内省,当然也不应该避免使用少量的“样板代码”或避免更新现有代码。

下面是一些有关该主题的评论,但请注意 这些应视为黑客攻击,不应在任何类型的生产代码中使用 。但是,它们可以在Swift中显示运行时内省的一些示例用法。

Equatable class / struct“wrapper”,使用运行时内省进行逐个属性的属性测试

你可以实现一个Equatable - 容器来保存equatable类型的值,它可以(不同于Equatable本身)被转换为,我们将用它来比较say的属性,a classstruct

/*  Heterogeneous protocol acts as castable Equatable container used for
    property-by-property equality testing in EquatableConstruct   */
protocol PseudoEquatableType {
    func isEqual(to other: PseudoEquatableType) -> Bool
}

extension PseudoEquatableType where Self : Equatable {
    func isEqual(to other: PseudoEquatableType) -> Bool {
        if let o = other as? Self { return self == o }
        return false
    }
}

使用class / struct equatable“包装器”并使用运行时内省与Equatable实现(ab)的一致性:

/*  EquatableConstruct and its conformance to Equatable  */
protocol EquatableConstruct : Equatable { }
func ==<T: EquatableConstruct>(lhs: T, rhs: T) -> Bool {

    let mirrorLhs = Mirror(reflecting: lhs)
    let mirrorRhs = Mirror(reflecting: rhs)

    guard let displayStyle = mirrorLhs.displayStyle,
        (displayStyle == .struct || displayStyle == .class) else {

            print("Invalid use: type is not a construct.")
            return false
    }

    let childrenLhs = mirrorLhs.children.filter { $0.label != nil }
    let childrenRhs = mirrorRhs.children.filter { $0.label != nil }

    guard childrenLhs.count == childrenRhs.count else { return false }

    guard !childrenLhs.contains(where: { !($0.value is PseudoEquatableType) }) else {
        print("Invalid use: not all members have types that conforms to PseudoEquatableType.")
        return false
    }

    return zip(
        childrenLhs.flatMap { $0.value as? PseudoEquatableType },
        childrenRhs.flatMap { $0.value as? PseudoEquatableType })
        .reduce(true) { $0 && $1.0.isEqual(to: $1.1) }
}

使用示例

我们在示例中设置了一些非本机类型:

struct MyStruct {
    var myInt: Int = 0
    var myString: String = ""
}

class MyClass {
    var myInt: Int
    var myString: String
    var myStruct: MyStruct
    var myColor: UIColor

    init(myInt: Int, myString: String,
         myStruct: MyStruct, myColor: UIColor) {
        self.myInt = myInt
        self.myString = myString
        self.myStruct = myStruct
        self.myColor = myColor
    }
}

对于某些特定类型,例如MyClass,只有当类型本身(此处,EquatableConstruct)中的所有类型的不同属性都符合MyClass时,才可以使用PseudoEquatableType“equatable wrapper”: / p>

/* Extend (some/all) fundamental (equatable) Swift types to PseudoEquatableType  */
extension Bool : PseudoEquatableType {}    
extension Int : PseudoEquatableType {}
// ... Int8, UInt8, ..., Double, Float, ... and so on

extension String : PseudoEquatableType {}
extension UIColor: PseudoEquatableType {}

/* As a MyStruct instance is contained in MyClass, extend MyStruct to PseudoEquatableType
 to add the type to allowed property types in EquatableConstruct    */
extension MyStruct : PseudoEquatableType {}

/* Conformance to EquatableConstruct implies conformance to Equatable */
extension MyStruct : EquatableConstruct {}
extension MyClass : EquatableConstruct {}

测试EquatableMyStruct的自动MyClass一致性,符合EquatableConstruct

/* Example */
var aa = MyStruct()
var bb = MyStruct()

aa == bb            // true
aa.myInt = 1
aa == bb            // false

var a = MyClass(myInt: 10, myString: "foo",
                myStruct: aa, myColor: UIColor(white: 1.0, alpha: 1.0))
var b = MyClass(myInt: 10, myString: "foo",
                myStruct: aa, myColor: UIColor(white: 1.0, alpha: 1.0))

a == b              // true
a.myInt = 2
a == b              // false
b.myInt = 2
b.myString = "Foo"
a.myString = "Foo"
a == b              // true
a.myStruct.myInt = 2
a == b              // false
a.myStruct.myInt = 1
a == b              // true
a.myColor = UIColor(white: 0.5, alpha: 1.0)
a == b              // false