我想配置一个具有多个表现形式的对象;我为他们创建了协议;这样我就可以将它们组合成一个具体的结构。
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.swift
,ConcretePresentable.swift
保存具体结构和Presentable.swift
保存协议),我得到{{1 }}
为什么会这样?
答案 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)
问题在于Presentable
到TextAndImagePresentable
的演员表。 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
不符合这两种协议就不会发生任何事情,那么在方法签名上划分它就更有意义了。