使用协议组合

时间:2016-02-04 10:51:14

标签: swift protocols

我想配置一个具有多个表现形式的对象;我为他们创建了协议;这样我就可以将它们组合成一个具体的结构。

protocol Presentable {

}

protocol TextPresentable: Presentable {
    var text:String { get }
}

protocol ImagePresentable: Presentable {
    var image:String { get }
    var images:[String] { get }
}

具体结构:

struct ConcretePresentable: TextPresentable, ImagePresentable {
    var text:String { return "Text" }
    var image:String { return "Image" }
    var images:[String] { return ["A", "B"] }
}

接下来会有一个演示者,我会检查传入的演示者是否真的有效:

typealias TextAndImagePresentable = protocol<TextPresentable, ImagePresentable>

struct ConcretePresenter {
    func configureWithPresentable(presentable: Presentable) {
        guard let textAndImagePresentable = presentable as? TextAndImagePresentable else {
            return
        }

        print(textAndImagePresentable.text)
        print(textAndImagePresentable.image)
        print(textAndImagePresentable.images)
    }
}

配置:

    let concretePresentable = ConcretePresentable()
    let concretePresenter = ConcretePresenter()

    concretePresenter.configureWithPresentable(concretePresentable)

如果我在游乐场中运行它,并且所有代码都在同一个地方,那就没问题了。但是,一旦我把它放在一个项目中并将其拆分为多个文件(ConcretePresenter.swiftConcretePresentable.swift保存具体结构和Presentable.swift保存协议),我得到{{1 }}

为什么会这样?

4 个答案:

答案 0 :(得分:2)

作为免责声明,我不一定觉得这个答案非常令人满意,但确实工作。

所以,一旦我注意到文字&amp;图像属性在每个其他位置返回(text属性返回image属性的值,反之亦然),我认为问题有某些事情与Swift在这里管理指针的工作正在做什么。

因此,出于好奇,我想为协议添加一个真正的标量值。我在value协议中添加了Int属性作为TextPresentable

protocol Presentable {}

protocol TextPresentable: Presentable {
    var text:String { get }
    var value: Int { get }
}

protocol ImagePresentable: Presentable {
    var image:String { get }
    var images:[String] { get }
}

然后我设置具体实现来返回一些已知值。在此,我们将返回0

struct ConcretePresentable: TextPresentable, ImagePresentable {
    var text:String { return "SomeText" }
    var value: Int { return 0 }
    var image:String { return "SomeImage" }
    var images:[String] { return ["A", "B"] }
}

运行此代码后,我们仍会遇到相同的崩溃,但我注意到value,打印0时确实不应该出现问题,而是打印一些非常大的数字:{ {1}}。这根本不对。

我还将4331676336从数组更改为字典,以查看错误是否仍然存在 - 确实如此。似乎崩溃与集合有关,而不是特定于数组。

从这里开始,我尝试了其他一些东西。

我尝试使images成为一个类,而不是一个结构。

ConcretePresentable

这导致了相同的行为。

我尝试使class ConcretePresentable: TextPresentable, ImagePresentable 符合typealias而不是协议:

ConcretePresentable

这导致了相同的行为。

我试着同时做上述两件事:

struct ConcretePresentable: TextAndImagePresentable

但仍然是相同的行为。

我确实提出了一种方法来使它工作。制定符合class ConcretePresentable: TextAndImagePresentable 中两个协议的协议,并使typealias符合:

ConcretePresentable

这里的问题是,如果您没有明确地使protocol TextAndImagePresentable: TextPresentable, ImagePresentable {} struct ConcretePresentable: TextAndImagePresentable { // ... } 符合协议,即使它符合ConcretePresentable和{{guard let,它也会失败TextPresentable 1}}。

答案 1 :(得分:1)

struct使用值语义,因此复制属性。 Swift应该将此报告为错误,因为您尝试从两个派生自相同基本协议的协议继承。在类中这将起作用,但在struct中它不会因为struct的值语义。如果你决定将一个变量添加到Presentable协议,那么Swift会混淆哪些变量带入结构。来自TextPresentable或ImagePresentable

您应该使用@protocol Presentable : class,然后将ConcretePresentable转换为类来解决此问题。

protocol Presentable : class {
}

class ConcretePresenter {...

答案 2 :(得分:1)

我在 Swift用户上询问了这个问题并填写了一个错误。

确认是编译器中的错误:

https://bugs.swift.org/browse/SR-4477

现在由Joe Groff修正:

  

Joe Groff添加了评论 - 2小时前

     

合并。应该在将来的快照中修复。

答案 3 :(得分:0)

问题在于PresentableTextAndImagePresentable的演员表。 guard let成功,但会产生无效值(我不确切知道原因)。

检查它的一种方法是在执行命令时查看控制台:

print(textAndImagePresentable.text)
print(textAndImagePresentable.image)
print(textAndImagePresentable.images)

那将打印:

Image
Text
Program ended with exit code: 9

避免它的一种方法是更改​​方法签名以避免转换:

func configureWithPresentable(presentable: TextAndImagePresentable) {
    print(presentable.text)
    print(presentable.image)
    print(presentable.images)
}

而且,在我看来,如果presentable不符合这两种协议就不会发生任何事情,那么在方法签名上划分它就更有意义了。