反射相等性是一组全局函数,允许按值比较两个 Swift 'Any' 实例。
通过递归遍历它们的属性和子属性,以及它们的父类(以及父类的父类等)的属性和子属性来比较值,结合了 Swift.Mirror
、Swift.String(describing:)
和 Objective C 运行时的反射功能。如果所有属性值都匹配,则认为它们相等,即使类实例不相同(即,!==
)。
没有办法证明它在一般情况下有效,而且不可避免地会出现边缘情况,产生虚假结果。因此,输出仅保证针对用于驱动包开发的测试用例类型是正确的。
如果您不确定是否需要它,那么您几乎肯定不需要!它最适合用于非常特殊的情况,您可以确定它会对您有效,并且这项工作实际上应该首先完成。虽然它仍在积极开发中(欢迎提出建议),但请将其视为一种好奇心,而不是其他东西!
该框架提供三个主要的公共函数
public func haveSameValue(_ args: [Any]) -> Bool
public func haveSameValue(_ lhs: Any, _ rhs: Any) -> Bool
public func deepDescription(_ instance: Any) -> String
这些可以如下使用
import ReflectiveEquality
class MyClass {
func myMethod() {
_ = haveSameValue("cat", "cat") // true
_ = haveSameValue(["cat", "cat", "cat", "cat"] // true
_ = haveSameValue("cat", "bat") // false
}
}
显然,上面的示例是可比较的,因此使用 String
实例纯粹是为了示例 - 显然不需要额外的包来执行此操作。
稍微有趣的是,比较无法通过 ==
可靠地进行的符合 Equatable
的类型
import ReflectiveEquality
import XCTest
class MyClass: XCTestCase {
func testComplexNSAttributedString() {
typealias ReadingOption = NSAttributedString.DocumentReadingOptionKey
typealias DocumentType = NSAttributedString.DocumentType
var parsingOptions: [ReadingOption: Any] {
[ReadingOption.documentType: DocumentType.html]
}
var parsedHTML: NSAttributedString {
NSAttributedString(html: giantHTML.data(using: .utf8)!,
options: parsingOptions,
documentAttributes: nil)!
}
XCTAssertEqual(deepDescription(parsedHTML), deepDescription(parsedHTML)) // ✅
XCTAssertEqual(parsedHTML, parsedHTML) // ❌
}
}
这种情况适用于各种 NS… 类。也许你想看看两个 NSFont
实例是否是相同的字体?或者诸如此类的东西。
这是一个用于 deepDescription
的不寻常用例
import ReflectiveEquality
class MyClass: NSObject {
func myMethod() {
_ = "cat" is NSObject // true!
_ = isNSObject("cat") // false!
_ = isNSObject(self) // true!
}
func isNSObject(_ instance: Any) -> Bool {
deepDescription(instance).contains("NSObject")
}
}
Swift 有一种很好的方式告诉你,各种各样的东西都是 NSObject
。它们在某种程度上是。又不是。也许吧。但实际上,如果我问它 struct Swift.String { }
是否是 NSObject
,在纯 Swift 上下文中,我更希望它直接说不。而 ReflectiveEquality 就是这样做的。
Reflective Equality 还具有一组完全实验性的函数,用于探测 ObjC 属性和 ivar
extension NSObject {
public var propertiesAndIvars: [String: Any]
public var ivars: [String: Any]
public var properties: [String: Any]
public var propertyAndIvarValues: [Any]
public var propertyValues: [Any]
public var ivarValues: [Any]
}
properties
没问题,但是当您尝试从 Swift 探测它们时,ivars
经常会发生可怕的崩溃,出现 EXC_BAD_ACCESS
。所以,尽情地搞破坏吧!