Context

考虑Swift 5.9中的这个协议和类:

protocol SpinnerHosting: AnyObject
{
    var spinnerItem: SpinnerItem { get }
}
final class MyViewController: NSViewController
{
    var activeChild: (NSViewController & SpinnerHosting)? = nil

    
    func pushChild(_ incomingChild: (NSViewController & SpinnerHosting))
    {
        // #1
        if incomingChild != activeChild {
           ...
        }

        // #2
        if incomingChild != activeChild! {
           ...
        }
    }
}

Problem

在第1点,SWIFT抛出以下错误:

Type 'any NSViewController & SpinnerHosting' cannot conform to 'Equatable'

在第2点(忽略不安全的展开),它抛出以下一个:

Binary operator '!=' cannot be applied to two 'any NSViewController & SpinnerHosting' operands 

Question:

我明白为什么协议不是Equatable.但我不明白为什么编译器认为这不是.在这里,SpinnerHosting被限制为AnyObject,这意味着引用类型,因此指针相等.但如果我将议定书限制在NSViewController本身,同样的错误仍然存在,这让我感到惊讶,因为NSViewController是可以等同的.

我只是想说:"任何NSViewController要占用activeChild的东西都必须有spinnerItem的属性."(我意识到我可以用子类来做到这一点;这不是我的问题.)

我刚刚在几个Apple源代码示例中看到了这种模式:var foo: ([Class] & [Protocol]),我看不出有什么好的理由不能是Equatable.

推荐答案

activeChild should be Equatable

你说得对,NSViewController是从NSObject继承的,NSObjectEquatable是一致的. 所以,NSViewController的每个子类仍然是Equatable.

因此,无论其他一致性要求如何,类型为NSViewController的变量仍然是Equatable.

事实上,这段代码编译得很好.

protocol SpinnerHosting: AnyObject { }
class MyViewController: NSViewController, SpinnerHosting { }
var a: (NSViewController & SpinnerHosting) = MyViewController()
var b: (NSViewController & SpinnerHosting) = a
print(a == b)

❌当我们将a%和b%设置为可选项时,问题就出现了.

protocol SpinnerHosting: AnyObject { }
class MyViewController: NSViewController, SpinnerHosting { }
var a: (NSViewController & SpinnerHosting)? = MyViewController()
var b: (NSViewController & SpinnerHosting)? = a
print(a == b) // Type 'any NSViewController & SpinnerHosting' cannot conform to 'Equatable'

现在我们得到你的错误👆

Wait, but Swift Conditional Conformance allows us to compare optional!

条件一致性最显著的好处是,存储其他类型(如数组或可选)的类型能够符合Equatable协议.

https://www.swift.org/blog/conditional-conformance/

对,如果2个可选项包含2个可以相等的泛型元素,则可以将它们等同

~

protocol SpinnerHosting: AnyObject { }
class MyViewController: NSViewController, SpinnerHosting { }
var a: NSViewController? = MyViewController()
var b: NSViewController? = a
print(a == b)

然而,当涉及Existential TypesConditional Conformance时,SWIFT编译器似乎无法推断Equatable符合性.

✅您可以帮助SWIFT编译器添加显式强制转换

protocol SpinnerHosting: AnyObject { }
class MyViewController: NSViewController, SpinnerHosting { }
var a: (NSViewController & SpinnerHosting)? = MyViewController()
var b: (NSViewController & SpinnerHosting)? = a
print(a as NSViewController? == b as NSViewController?)

现在它起作用了.

enter image description here

Here's another example

我们可以做一个测试,用Array代替Optional(这与Conditional Conformance有类似的好处).

✅将编译此代码.

let list: [NSViewController] = []
list == list

但是,当我们把存在主义类型加入到聚会中时,

let list: [NSViewController & Codable] = []
list == list // Type 'any NSViewController & Codable' (aka 'any NSViewController & Decodable & Encodable') cannot conform to 'Equatable'

希望能有所帮助.

Swift相关问答推荐

为什么Swift在某些链调用中不能对不可变值使用变异成员,而在其他链调用中则不能使用变异成员?

如何使用RxSwift根据数据数绘制UICollectionView单元格

如何消除SwiftUI中SF符号的填充

如何获取SCNCamera正在查看的网格点(SCNNode)的局部坐标和局部法线?

当TextField变为空时,不会触发onChange

SWIFT计划计时器方法在时间间隔后未被调用

如何为我的项目设置生命周期选项?

Swift:结果的失败类型不能是协议 - Type 'any ShadowError' cannot conform to Error

如何在 swift 5.0 中获取当前行

Firebase removeObserver 在 Swift 5 中不起作用

如何从数据中读取以空结尾的字符串?

iOS SwiftUI - 无法转换 ObservedObject 类型的值

(SwiftLint)如果有正文,如何编写规则(可能是自定义),在{之后总是\n(换行)?

展开 List 中的 HStack 以端到端但不是从上到下,因此看起来项目之间有更宽的空间

Vapor Swift 如何配置客户端连接超时

Xcode 13.3 将函数调用更改为属性访问

如何在 SwiftUI 中打开 ImagePicker?

如何在 Swift 中将 UILabel 居中?

你如何在 UIBarItem 中使用 setTitleTextAttributes:forState?

Swift UITableView reloadData 在一个闭包中