Swift中为何‘String?’类型不遵循Equatable协议?
这个问题我之前也踩过坑,先给你吃个定心丸:String? 本身是完全遵循 Equatable 协议的——从Swift 4.1开始,只要可选类型的包装类型(比如这里的String)遵循Equatable,Swift就会自动为Optional合成Equatable的实现,所以理论上用String?作为Observable的泛型参数是没问题的。那报错大概率是下面这几个原因之一:
1. 你的Swift版本太低了
如果你的项目还在使用Swift 4.0或更早的版本,可选类型不会自动合成Equatable的实现,这时候String?确实不遵循Equatable,自然会报错。
解决办法:直接把项目的Swift版本升级到4.1及以上(现在大部分项目都是Swift 5+了,升级后还能享受到更多语言特性)。要是实在没法升级,也可以手动给Optional扩展Equatable,但这种方式不推荐,容易有冲突:
extension Optional: Equatable where Wrapped: Equatable { public static func == (lhs: Optional<Wrapped>, rhs: Optional<Wrapped>) -> Bool { switch (lhs, rhs) { case (nil, nil): return true case let (l?, r?): return l == r default: return false } } }
2. Observable类的泛型约束写法有问题
先看一个正确的Observable类写法,你可以对比下自己的代码:
class Observable<T: Equatable> { private var _value: T { didSet { // 只有值变化时通知订阅者 guard _value != oldValue else { return } notifySubscribers() } } private var subscribers: [(T) -> Void] = [] init(initialValue: T) { self._value = initialValue } func subscribe(_ handler: @escaping (T) -> Void) { // 订阅时先发送当前值 handler(_value) subscribers.append(handler) } private func notifySubscribers() { subscribers.forEach { $0(_value) } } // 对外暴露的更新方法 func update(to newValue: T) { self._value = newValue } }
如果你的类的泛型约束写错了位置(比如把<T: Equatable>写在了某个方法上而不是类定义上),或者约束里有其他冲突的协议要求,就可能导致编译器无法识别String?的Equatable实现。
3. 你自定义了冲突的Equatable扩展
如果你在代码里手动给Optional写了Equatable扩展,而且实现有问题(比如限制了特定的Wrapped类型,或者逻辑错误),就会覆盖Swift自动合成的实现,导致编译器无法正确识别String?的Equatable合规性。
检查一下你的代码里有没有类似这样的扩展:
// 这种写法会限制只有Wrapped为String的Optional才遵循Equatable,会导致其他可选类型或者全局的Optional Equatable实现出问题 extension Optional: Equatable where Wrapped == String { // ...错误的实现... }
如果有的话,删掉这类自定义扩展就好了。
快速验证方法
你可以先写一段简单的测试代码,直接判断两个String?是否相等:
let optionalStr1: String? = "test" let optionalStr2: String? = "test" let optionalStr3: String? = nil print(optionalStr1 == optionalStr2) // 应该输出true print(optionalStr1 == optionalStr3) // 应该输出false
如果这段代码能正常编译运行,说明你的环境里String?是遵循Equatable的,那问题肯定出在你的Observable类的写法上,照着上面的正确示例调整就行。
内容的提问来源于stack exchange,提问作者yu shirota

