如何在 Swift 中使用具有相同协议变量的多个协议?

How to use multiple protocols in Swift with same protocol variables?

在 swift 中,我正在实施两个协议,GADCustomEventInterstitialGADCustomEventBanner

这两个协议都需要一个名为 delegate 的 属性。 delegate在每个协议中都是不同的类型,因此会产生冲突。

 class ChartBoostAdapter : NSObject, GADCustomEventInterstitial, GADCustomEventBanner, ChartboostDelegate{
        var delegate:GADCustomEventInterstitialDelegate?; // Name conflict
        var delegate:GADCustomEventBannerDelegate?; // Name conflict
         override init(){

        }
    ...

    }

简单的回答是你不能。

也许一个协议依赖于另一个协议,在这种情况下,您将使用依赖协议作为您的委托类型。

They are libraries/frameworks it's not my definition

那么显然你不能让同一个class同时采用两种协议。但你真的不需要。只需将此功能分成两个不同的 classes,这显然是这些协议的设计者的意图。您应该有一个 class 采用 GADCustomEventInterstitial 并具有 its delegate 另一个 class采用 GADCustomEventBanner 并具有 delegate。你有什么理由试图强迫它们成为同一个 class?与使用框架的所有事情一样,不要对抗框架,服从它。

其实是可以的,我刚遇到同样的情况。我有两个不同但有点相关的协议。在某些情况下,我需要两者都由委托实现,而在其他情况下,我只需要一个,我不想拥有两个属性,例如... delegate1, delegate2.

您需要做的是创建另一个继承自这两个协议的组合协议:

protocol ChartBoostAdapterDelegate: GADCustomEventInterstitialDelegate, GADCustomEventBannerDelegate { }


class ChartBoostAdapter : NSObject, GADCustomEventInterstitial, GADCustomEventBanner, ChartboostDelegate {

    weak var delegate: ChartBoostAdapterDelegate?

    override init(){

    }
    ...

}

请注意,如果您处于 Swift-only 环境中,这可以使用 Mixins 解决(自 Swift 2.0 起可能)。只要你需要将代码桥接到 Obj-C 就无法解决,因为此问题在 Obj-C 中无法解决。然而,这通常可以通过包装器 class 来解决,稍后我将展示它。

让我们将其分解为一个最简单的例子:

import Foundation

@objc
protocol ProtoA {
    var identifier: String { get }
}

@objc
protocol ProtoB {
    var identifier: UUID { get }
}

@objc
class ClassA: NSObject, ProtoA, ProtoB {
    let identifier = "ID1"
    let identifier = UUID()
}

上面的代码将失败,因为没有两个属性可以具有相同的名称。如果我只声明 identifier 一次并使它成为 String,编译器会抱怨 ClassA 不符合 ProtoB,反之亦然。

但这里是 Swift-only 实际有效的代码:

import Foundation

protocol ProtoA {
    var identifier: String { get }
}

protocol ProtoB {
    var identifier: UUID { get }
}

class ClassA {
    let stringIdentifier = "ID1"
    let uuidIdentifier = UUID()
}

extension ProtoA where Self: ClassA {
    var identifier: String {
        return self.stringIdentifier
    }
}

extension ProtoB where Self: ClassA {
    var identifier: UUID {
        return self.uuidIdentifier
    }
}

extension ClassA: ProtoA, ProtoB { }

当然,你不能那样做:

let test = ClassA()
print(test.identifier)

编译器会说 ambigous use of 'identifier',因为它不知道您要访问哪个标识符,但您可以这样做:

let test = ClassA()
print((test as ProtoA).identifier)
print((test as ProtoB).identifier)

输出将是

ID1
C3F7A09B-15C2-4FEE-9AFF-0425DF66B12A

符合预期。

现在要将 ClassA 实例公开给 Obj-C,您需要包装它:

class ClassB: NSObject {
    var stringIdentifier: String { return self.wrapped.stringIdentifier }
    var uuidIdentifier: UUID { return self.wrapped.uuidIdentifier }

    private let wrapped: ClassA

    init ( _ wrapped: ClassA )
    {
        self.wrapped = wrapped
    }
}

extension ClassA {
    var asObjCObject: ClassB { return ClassB(self) }
}

如果您将它直接放入 ClassA 的 class 声明中,您甚至可以将其存储为 属性,这样您就不必再次重新创建它但这会使一切变得复杂,因为 ClassB 可能只持有对包装对象的弱引用,否则你会创建一个保留循环,并且两个对象都不会被释放。最好将其缓存在 Obj-C 代码中的某处。

为了解决您的问题,可以使用类似的包装器方法构建一个大师 class 并且这位大师 class 分发了两个包装器 class,一个符合 GADCustomEventInterstitial 和一个符合 GADCustomEventBanner 但它们没有任何内部状态或逻辑,它们都使用主 class 作为存储后端并将所有请求传递给实现的 class所有必需的逻辑。