除了诊断之外,您不应使用运行时自省,当然也不应避免使用少量“样板”代码或避免更新现有代码。
然而,下面是一些关于该主题的 cmets,但请注意,这些应被视为 hack,不应在任何类型的生产代码中使用。但是,它们可以展示 Swift 中运行时自省的一些示例用法。
Equatable class/struct "wrapper",使用运行时内省进行逐个属性相等性测试
你可以实现一个Equatable-container 来保存相等类型的值,它可以(不同于Equatable 本身)被强制转换为,我们将使用它来比较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 的一致性(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时才可以使用:
/* 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 {}
测试MyStruct 和MyClass 的自动Equatable 一致性,由它们与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