我们在Objective-C
时代,通常使用 - isEqualToString:
来在已经能确定比较对象和待比较对象都是NSString
的时候进行字符串判等。Swift 中的String 类型
中是没有- isEqualToString:
或者 - isEqual:
这样的方法的,因为这些毕竟是NSObject
的东西。Swift 的字符串内容判等,我们简单的使用== 操作符
来进行:
let str1 = "快乐的字符串"
let str2 = "快乐的字符串"
let str3 = "开心的字符串"
str1 == str2 // true
str1 == str3 // false
在判等上Swift 的行为和Objective-C 有着巨大的差别。在Objective-C 中==
这个符号的意思是判断两个对象是否指向同一块内存地址
。其实很多时候这并不是我们所期望的判等,我们更关心的往往还是对象的内容相同,而这种意义上的相等即使两个对象引用的不是同一块内存的地址时,也可以做到的。在Objective-C 中我们通常通过 - isEqusl:
进行重写,或者更进一步去实现类似 - isEqualToString:
这样的 - isEqualToClass:
的带有类型信息的方法来进行内容判断。如果我们没有在任意子类重写 - isEqual:
的话,在调用这个方法时会直接使用NSObject 中的版本,去直接进行Objective-C 的==
判断。
在Swift 中情况大不一样,Swift 里的==
是一个操作符的声明,在Equatable
里声明了这个操作符的接口方法:
public protocol Equatable {
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static func == (lhs: Self, rhs: Self) -> Bool
}
实现这个接口的类型需要定义适合自己类型的== 操作符
,如果我们认为两个输入有相等关系的话,就应该返回true
。实现了Equatable 的类型
就可以使用 ==
及 != 操作符
来进行相等判定了(在实现时我们只需要
实现==
,!= 则由标准库自动取反实现
)。这和原来Objective-C
的isEqual:
的行为十分相似,比如我们在一个待办事项应用中,从数据库中取得使用uuid 进行编号的待办条件,在实践中我们一般考虑使用这个uuid 来判定两个条目对象是不是同一条目。让这个表示条目的TodoItem 类实现Equatable 接口:
class TodoItem {
let uuid: String
var title: String
init(uuid: String, title: String) {
self.uuid = uuid
self.title = title
}
}
extension TodoItem: Equatable {
}
func ==(lhs: TodoItem, rhs: TodoItem) -> Bool {
return lhs.uuid == rhs.uuid
}
对于== 的实现
我们并没有像实现其他一些接口一样将其放在对应的extension 里
,而是放在了全局的scope
中。这是合理的做法,因为你应该需要在全局范围
内都能使用==
。事实上,Swift 的操作符都是全局的,关于操作符的更多信息,可以参看操作符。
Swift 的基本类型
都重载
了自己对应
版本的==
,而对于NSObject 的子类
来说,如果我们使用==
并且没有
对于这个子类的重载
的话,将转为调用这个类的 - isEqual: 方法
。这样如果这个NSObject 子类原来就实现了 - isEqual:
的话,直接使用==
并不会造成它的Swift 类型的行为差异
;但是如果无法找到合适的重写的话,这个方法就将回滚使用最初的NSObject 里的实现,对引用对象地址进行直接比较
。因为对于NSObject 子类的判等你有两种选择:要么重载==
,要么重写 -isEqual
:。如果你只是在Swift
中使用的话,两种方式是等效的
,但是如果你还需要在Objective-C 中使用这个类的话,因为Objective-C 不接受操作符重载
,只能使用 - isEqual:
这时你应该考虑使用第二种方式。
对于原来Objective-C
中使用== 进行的对象指针
的判定,在Swift 中提供的是另一个操作符===
。在Swift 中===
只有一种重载:
@inlinable public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool
它用来判断两个AnyObject 是否是同一个引用。
let string: NSString = "aaaa"
let name: NSString = "aaa"
if string === name {
print("相同地址")
}
对于判等,和它紧密相关的一个话题就是哈希,因为哈希是一个稍微复杂的话题,所以我将它单独写成一篇。但是如果在实际项目中你需要重载==
或者重写 - isEqual:
来进行判等的话,很可能你也会想看看有关哈希的内容,重载了判等的话,我们还需要提供一个可靠的哈希算法
,使得判等的对象在字典中作为key
是不会发生奇怪的事情。