我正在尝试创建一个简单的协议,说明对象是处于“开启”状态还是“关闭”状态。对此的解释取决于实施对象。对于UISwitch
,无论开关是打开还是关闭(duh)。对于UIButton
,可能是按钮是否处于selected
状态。对于Car
,可能是汽车的引擎是否打开,或者即使它是否在移动。所以我开始创建这个简单的协议:
protocol OnOffRepresentable {
func isInOnState() -> Bool
func isInOffState() -> Bool
}
现在我可以扩展前面提到的UI控件:
extension UISwitch: OnOffRepresentable {
func isInOnState() -> Bool { return on }
func isInOffState() -> Bool { return !on }
}
extension UIButton: OnOffRepresentable {
func isInOnState() -> Bool { return selected }
func isInOffState() -> Bool { return !selected }
}
现在我可以创建这些类型的对象的数组并循环检查它们是否打开或关闭:
let booleanControls: [OnOffRepresentable] = [UISwitch(), UIButton()]
booleanControls.forEach { print($0.isInOnState()) }
大!现在我想创建一个将这些控件映射到UILabel
的字典,以便在控件更改状态时更改与控件关联的标签文本。所以我去宣布我的字典:
var toggleToLabelMapper: [OnOffRepresentable : UILabel] = [:]
// error: type 'OnOffRepresentable' does not conform to protocol 'Hashable'
哦!对!傻我。好吧,让我只使用协议组合更新协议(毕竟,我想在这里使用的控件都是Hashable:UISwitch,UIButton等):
protocol OnOffRepresentable: Hashable {
func isInOnState() -> Bool
func isInOffState() -> Bool
}
但是现在我得到了一组新的错误:
error: protocol 'OnOffRepresentable' can only be used as a generic constraint because it has Self or associated type requirements
error: using 'OnOffRepresentable' as a concrete type conforming to protocol 'Hashable' is not supported
好的......所以我做了一些堆栈溢出挖掘和搜索。我发现许多文章看起来很有希望,比如Set and protocols in Swift,Using some protocol as a concrete type conforming to another protocol is not supported,我发现type erasure
上有一些好文章似乎正是我需要的:{{3 }},http://krakendev.io/blog/generic-protocols-and-their-shortcomings和http://robnapier.net/erasure仅举几例。
这是我陷入困境的地方。我已经尝试过阅读所有这些内容,并且我已经尝试创建一个类Hashable
并且也符合我的OnOffRepresentable
协议的类,但我无法弄清楚如何完成所有这些连接。
答案 0 :(得分:3)
我不知道我是否必须使OnOffRepresentable
协议继承自Hashable
。它似乎不是你想要表示为开启或关闭的东西必须也是可以清除的。因此,在下面的实现中,我只将Hashable
一致性添加到类型擦除包装器中。这样,您可以尽可能直接引用OnOffRepresentable
项(没有“只能在通用约束中使用”警告),并且只在需要放置它们时将它们包装在HashableOnOffRepresentable
类型的橡皮擦中in sets或将它们用作字典键。
protocol OnOffRepresentable {
func isInOnState() -> Bool
func isInOffState() -> Bool
}
extension UISwitch: OnOffRepresentable {
func isInOnState() -> Bool { return on }
func isInOffState() -> Bool { return !on }
}
extension UIButton: OnOffRepresentable {
func isInOnState() -> Bool { return selected }
func isInOffState() -> Bool { return !selected }
}
struct HashableOnOffRepresentable : OnOffRepresentable, Hashable {
private let wrapped:OnOffRepresentable
private let hashClosure:()->Int
private let equalClosure:Any->Bool
var hashValue: Int {
return hashClosure()
}
func isInOnState() -> Bool {
return wrapped.isInOnState()
}
func isInOffState() -> Bool {
return wrapped.isInOffState()
}
init<T where T:OnOffRepresentable, T:Hashable>(with:T) {
wrapped = with
hashClosure = { return with.hashValue }
equalClosure = { if let other = $0 as? T { return with == other } else { return false } }
}
}
func == (left:HashableOnOffRepresentable, right:HashableOnOffRepresentable) -> Bool {
return left.equalClosure(right.wrapped)
}
func == (left:HashableOnOffRepresentable, right:OnOffRepresentable) -> Bool {
return left.equalClosure(right)
}
var toggleToLabelMapper: [HashableOnOffRepresentable : UILabel] = [:]
let anySwitch = HashableOnOffRepresentable(with:UISwitch())
let anyButton = HashableOnOffRepresentable(with:UIButton())
var switchLabel:UILabel!
var buttonLabel:UILabel!
toggleToLabelMapper[anySwitch] = switchLabel
toggleToLabelMapper[anyButton] = buttonLabel
答案 1 :(得分:0)
使用associatedType
创建协议(或使其与associatedType
Hashable
之类的protocol OnOffRepresentable {
func isInOnState() -> Bool
func isInOffState() -> Bool
}
符合其他协议的协议将使该协议与泛型非常不相关。
我建议你一个非常简单的解决方法
首先,我们不需要2个完全相反的功能? ;)
所以这个
protocol OnOffRepresentable {
var on: Bool { get }
}
变成这个
extension UISwitch: OnOffRepresentable { }
extension UIButton: OnOffRepresentable {
var on: Bool { return selected }
}
当然
OnOffRepresentable
现在我们无法将Key
用作Dictionary
的{{1}},因为我们的协议必须是Hashable
。然后让我们使用另一种数据结构!
let elms: [(OnOffRepresentable, UILabel)] = [
(UISwitch(), UILabel()),
(UIButton(), UILabel()),
]
那就是它。