【问题标题】:Comparing Two Protocol Instances for Equality in Swift在 Swift 中比较两个协议实例的相等性
【发布时间】:2020-02-16 19:50:13
【问题描述】:

这是交易,

我正在编写一个 SDK,我想将观察者声明为协议,而不是类或结构(它有点像“观察者/委托”混合体)。

我希望能够比较作为协议引用传入的两个参数,而不是它们实际上是 IRL 的具体类/结构。

我知道进行比较的“简单”方法是将协议限制为HashableEquatable,但我想避免给用户带来负担(这是一个 SDK)。

这是一个小操场,我的意思是:

protocol A {
    func AFunc() -> String
}

class APrime: A {
    func AFunc() -> String { "I AM GROOT" }
}

let variableA = APrime()
let variableB = APrime()

func compareTypes(_ inA: A, _ inB: A) -> String {
//    if inA == inB {
//        return ""
//    }
    return "not "
}

print("A is \(compareTypes(variableA, variableB))B.")

print("A is \(compareTypes(variableA, variableA))A.")

不修边幅的部分是compareTypes(_: A, _: A) 中的注释部分。我需要弄清楚如何在不进入“Hacksylvania”的情况下比较它们,我可以通过比较每个实例中 AFunc() 的地址来做到这一点。

预期的输出是:

A is not B.
A is A.

对于更“快速”的方法有什么想法吗?我一定是只见树木不见森林。

【问题讨论】:

  • 假设 ClassA 和 ClassB 符合协议 A,并且您分别拥有这些类的 ab 实例。比较它们是否等于 qua 协议 A 可能意味着什么?除非您能保证ab 是同一类型,否则您甚至无法开始。 — 请注意,这正是 Swift 5.1 some A 解决的问题。如果您不想走那条路,则需要进行某种类型擦除,以便您比较的是 A-wrapper 类实例。
  • 是的。这就是我正在看的。 “一些”很好,但我不确定它是否能解决问题。我正在寻找 identity 平等,而不是 type 平等。我有一种感觉,我最终不得不强制协议是平等的,但我想避免这种情况。
  • 我认为您不太接受 what 是平等的。我要说的是 equatblity qua 协议的整个概念是不连贯的。协议不是一个东西。 协议采用者类型的实例是一个东西。您的标题“协议实例”的整个措辞似乎表明未能抓住这一点。没有“协议实例”之类的东西。
  • 另一种可能是强制实现者提供一个 UUID 变量,该变量获取 SDK 分配的唯一 UUID。我以前做过。它很笨拙,但很有效。
  • “我正在寻找身份平等,而不是类型平等。”这就是===,它只适用于类。 Structus 没有身份,因为它们在记忆中不断移动。结构的副本导致值的副本。类的副本会导致其引用的副本,但两个引用仍然指向堆中相同的唯一(就“身份”和内存位置而言)对象。

标签: swift protocols equatable


【解决方案1】:

只是为此添加一些闭包,这是我解决此问题的方法:

protocol A {
    var uuid: Int { get } // This is the secret sauce. It will contain a unique UUID, associated with the instance.
    func AFunc() -> String
}

class APrime: A {
    let uuid: Int = Int.random(in: 0..<1000) // The UUID is initialized with the instance.
    func AFunc() -> String { "I AM GROOT" }
}

let variableA = APrime()
let variableB = APrime()
let variableC = variableA

func compareTypes(_ inA: A, _ inB: A) -> String {
    if inA.uuid == inB.uuid { // We compare UUIDs.
        return ""
    }
    return "not "
}

print("C is \(compareTypes(variableC, variableB))B.")

print("C is \(compareTypes(variableC, variableA))A.")

“uuid”变量通常是一个实际的UUID 类型,但我不想在示例中导入Foundation,所以我只是做了一个简单的rand。它明白了重点。

这个输出:

C is not B.
C is A.

还有另一种方式(有时我也会使用):

protocol B {
    func BFunc() -> String
    func amIThisOne(_ instanceToCompare: B) -> Bool // This is an identity comparator
}

class BPrime: B {
    func BFunc() -> String { "I AM GROOT'S BROTHER" }
    // We compare ourselves against the other instance, assuming it can be cast to our own type.
    func amIThisOne(_ inInstanceToCompare: B) -> Bool {
        guard let instanceToCompare = inInstanceToCompare as? Self else { return false }
        return self === instanceToCompare
    }
}

let variableD = BPrime()
let variableE = BPrime()
let variableF = variableD

print("D is \(variableE.amIThisOne(variableD) ? "" : "not ")E.")

print("D is \(variableD.amIThisOne(variableF) ? "" : "not ")F.")

哪些输出:

D is not E.
D is F.

这允许以更加程序化的方式比较实例。

怎么做

然后,当然,如果我们可以控制实例,我们就可以真正做到 Equatable 的事情(这需要操场导入 Foundation):

protocol C: Equatable {
    func CFunc() -> String
}

class CPrime: C {
    // This is actually not what I want, as I want to compare protocols, not conforming classes.
    static func == (lhs: CPrime, rhs: CPrime) -> Bool {
        guard let lhs = lhs as? Self else { return false }
        guard let rhs = rhs as? Self else { return false }

        return lhs === rhs
    }

    func CFunc() -> String { "I AM GROOT'S UDDER BROTHER" }
}

let variableG = CPrime()
let variableH = CPrime()
let variableI = variableG

print("G is \(variableG == variableH ? "" : "not ")H.")

print("G is \(variableI == variableG ? "" : "not ")I.")

哪些输出:

G is not H.
G is I.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-05
    • 1970-01-01
    相关资源
    最近更新 更多