像
class A :NSObject {
let a :Int
let b :UIColor
}
我不想通过逐个比较所有属性来实现isEqual
。如果是这样,当我添加另一个属性时,我应该再次修改isEqual
的工具。
在swift中使用Mirror
时,我可以方便地打印所有属性。如何使用isEqual
方便地实现Mirror
方法。
答案 0 :(得分:2)
除了诊断之外,您不应该使用运行时内省,当然也不应该避免使用少量的“样板代码”或避免更新现有代码。
下面是一些有关该主题的评论,但请注意 这些应视为黑客攻击,不应在任何类型的生产代码中使用 。但是,它们可以在Swift中显示运行时内省的一些示例用法。
class
/ struct
“wrapper”,使用运行时内省进行逐个属性的属性测试你可以实现一个Equatable
- 容器来保存equatable类型的值,它可以(不同于Equatable
本身)被转换为,我们将用它来比较say的属性,a class
或struct
。
/* 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 {}
测试Equatable
和MyStruct
的自动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