【问题标题】:Swift @objc protocol cannot be used as a type conforming to protocol 'Equatable' because 'Equatable' has static requirementsSwift @objc 协议不能用作符合协议 'Equatable' 的类型,因为 'Equatable' 具有静态要求
【发布时间】:2020-02-23 08:02:26
【问题描述】:

我目前正在用 Swift 编写一个可重用的 UI 组件,它应该可以从 Obj-C/Swift 世界中使用(这是一个混合项目)。我定义了一个没有任何关联类型的@objc 协议(因为@objc 协议不允许使用这些类型)。在组件中的一个方法中,我需要将协议存储为一种类型,并且需要找到特定条目的索引,有点类似于以下 -

func select<T: Itemable>(_ item: T) {
    guard let itemIndex = items.index(of: item) else {
        return
    }
  //more stuf
}

其中itemsItemable(协议)类型的数组。

但是,我收到错误消息,说我不能将其用作符合 Equatable 的类型,因为 equatable 具有静态要求。 Itemable 定义如下-

@objc protocol Itemable { //methods and properties }

另外,不确定如何使其符合 equatable。显然,以下内容有所帮助,但不确定原因-

func ==<T: <Itemable>>(lhs: T, rhs: T) -> Bool {
    return lhs.aProperty == rhs.aProperty
}

在我看来它可能需要擦除类型,但不知道如何去做。

这是协议的精简版,显示了所有不同类型的方法和属性 - 它实际上没有任何静态或关联类型。

 @objc protocol Itemable {
    typealias voidBlock = () -> Void
    var color: UIColor { get }
    var screenParameters: [String : String] { get }
    var screenView: String { get }
    var iconImage: UIImage? { get }
    @objc var accessibilityLabel: String? { get }
}

extension Array where Element: Itemable {
    func index(of element: Element) -> Int? {
        index(where: { $0.screenView == element.screenView })
    }
} 

【问题讨论】:

  • 您能否显示更多有关guard 语句的代码。你能显示它所在的方法/类吗?
  • 完成!请再看一遍。
  • 我猜items[Itemable]?在这种情况下,我认为一种方法(不确定是否是唯一方法)是做as NSArray 并在NSArray 中使用index(of:)

标签: objective-c swift generics protocols type-erasure


【解决方案1】:

您不能使 @objc 类型符合 Equatable。 Equatable 有一个自我要求。 Objective-C 协议不能表达 Self 要求。

您的== 函数可用,但它不会导致类型符合 Equatable。这只是意味着您可以评估item == item。但是,您不能调用 items.contain(item),因为 Itemable 不符合 Equatable。您可以调用items.contains{$0 == item},因为这只需要== 函数,而不是Equatable。当然,如果你想要的话,你可以为[Itemable] 实现一个自定义的.contains 方法。但它仍然不是 Equatable。

对于你的例子,我相信你想完全摆脱==,并使用这个:

guard let itemIndex = items.index(where: { $0.aProperty == item.aProperty }) else {

如果你经常这样做,当然可以在[Itemable] 上添加扩展名(未经测试):

extension Array where Element: Itemable {
    func index(of element: Element) -> Int? {
        firstIndex(where: { $0.aProperty == element.aProperty })
    }
}

那么你的原始代码就可以了。

与您的问题有些无关:这可能是简化的代码,但要非常小心== 的这种实现。首先,== 应该始终测试所有可见属性。例如,如果aProperty 只是 ID,那么这是实现== 的危险方式。当两件事相等时(在 ObjC 和 Swift 中),它们应该在所有上下文中都可以互换。如果你关心你拥有哪一个,那么它们并不是真正“平等”的。

此外,当两个事物相等时,它们应该具有相同的哈希(如果它们是 Equatable,它们必须具有相同的哈希)。

有关规则,请参阅Conforming to the Equatable Protocol 上的文档。虽然== 在技术上并不暗示 Equatable,但如果您不是指 Equatable,那么使用它会让人感到困惑。

【讨论】:

  • 谢谢,罗伯。当我考虑您的第一种方法(即在 where 子句中使用闭包)时,错误消失了,但使用第二种方法(即使用带有 where 子句的数组扩展名),我仍然得到相同的错误 - 'Itemable' 不能用作符合协议“Itemable”的类型,因为“Itemable”具有静态要求
  • 我无法重现这个。请显示产生此错误的实际代码。您的协议是否有静态要求?
  • 在问题中添加了协议的精简版。
  • 我无法使用此代码进行复制。它似乎编译得很好。你使用的是什么版本的 Xcode? index(where:) 已弃用(现在称为 firstIndex(where:)),因此如果您没有收到警告,则可能是在运行旧版本的 Swift。
猜你喜欢
  • 2020-12-04
  • 2016-09-29
  • 2017-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-02
相关资源
最近更新 更多