自定义NSTextLocation的NSTextRange如何正确比较相等性?
自定义NSTextLocation的NSTextRange相等性比较问题
当使用自定义NSTextLocation实现的NSTextRange调用isEqual(to:)方法时,即使起始和结束位置通过compare(_:)返回.orderedSame,也会判定为不相等。如何正确比较这类范围的相等性?
问题代码示例
class Location: NSObject, NSTextLocation { let value: Int func compare(_ location: NSTextLocation) -> ComparisonResult { guard let location = location as? Location else { fatalError("Expected Location") } if self.value == location.value { return .orderedSame } else if self.value < location.value { return .orderedAscending } else { return .orderedDescending } } init(_ value: Int) { self.value = value } } let textRange1 = NSTextRange(location: Location(1), end: Location(2))! let textRange2 = NSTextRange(location: Location(1), end: Location(2))! print("startLocations are equal: \(textRange1.location.compare(textRange2.location) == .orderedSame)") print("endLocations are equal: \(textRange1.endLocation.compare(textRange2.endLocation) == .orderedSame)") print("ranges are equal: \(textRange1.isEqual(to: textRange2))")
问题原因
NSTextRange的isEqual(to:)方法内部并非使用compare(_:)判断位置相等,而是直接调用NSObject的isEqual(_:)方法。默认的isEqual(_:)比较的是对象的内存地址,因此即使两个Location实例的value相同,也是不同的对象实例,导致NSTextRange判定为不相等。
解决方案
给自定义的Location类重写isEqual(_:)方法,根据value判断相等性;同时重写hash方法(这是NSObject子类遵循相等性约定的最佳实践):
class Location: NSObject, NSTextLocation { let value: Int func compare(_ location: NSTextLocation) -> ComparisonResult { guard let location = location as? Location else { fatalError("Expected Location") } if self.value == location.value { return .orderedSame } else if self.value < location.value { return .orderedAscending } else { return .orderedDescending } } override func isEqual(_ object: Any?) -> Bool { guard let other = object as? Location else { return false } return self.value == other.value } override var hash: Int { return value.hashValue } init(_ value: Int) { self.value = value } } let textRange1 = NSTextRange(location: Location(1), end: Location(2))! let textRange2 = NSTextRange(location: Location(1), end: Location(2))! print("startLocations are equal: \(textRange1.location.compare(textRange2.location) == .orderedSame)") print("endLocations are equal: \(textRange1.endLocation.compare(textRange2.endLocation) == .orderedSame)") print("ranges are equal: \(textRange1.isEqual(to: textRange2))") // 现在会输出true
结果验证
修改后运行代码,textRange1.isEqual(to: textRange2)会返回true,符合预期的相等性判断。
内容的提问来源于stack exchange,提问作者georgemp
相关产品推荐
相关产品推荐

