Swift - 判等

我们在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-CisEqual: 的行为十分相似,比如我们在一个待办事项应用中,从数据库中取得使用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 是不会发生奇怪的事情。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值