12

我正在尝试创建一个简单的协议,说明对象是处于“开启”状态还是“关闭”状态。对它的解释取决于实现对象。对于 a UISwitch,它是开关是打开还是关闭(duh)。对于 a UIButton,可能是按钮是否处于selected状态。对于 a 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()) }

伟大的!现在我想制作一个将这些控件映射到 a 的字典,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

好的...所以我做了一些堆栈溢出挖掘和搜索。我发现许多看起来很有希望的文章,例如Swift 中的 Set 和协议,支持将某些协议用作符合另一个协议的具体类型,并且我发现那里有一些很棒的文章type erasure似乎正是我需要的: http://krakendev.io/blog/generic-protocols-and-their-shortcomings , http://robnapier.net/erasurehttps://realm.io/news/type-erased-wrappers-in-迅速/仅举几例。

这是我卡住的地方。我已经尝试通读所有这些内容,并且尝试创建一个将Hashable符合并且也符合我的OnOffRepresentable协议的类,但我不知道如何将它们全部连接起来。

4

2 回答 2

3

我不知道我是否一定要让OnOffRepresentable协议继承自Hashable. 您似乎不想表示为开或关的东西也必须是可散列的。所以在我下面的实现中,我只将Hashable一致性添加到类型擦除包装器中。这样,您可以OnOffRepresentable尽可能直接引用项目(没有“只能在通用约束中使用”警告),并且仅在HashableOnOffRepresentable需要将它们放在集合中或将它们用作字典键时将它们包装在类型橡皮擦中。

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
于 2016-08-20T00:11:12.360 回答
0

创建一个带有 a 的协议associatedType(或使其符合另一个带有associatedTypelikeHashable的协议)将使该协议与泛型非常不友好。

我会建议你一个非常简单的解决方法

OnOff 可表示

首先,我们不需要两个完全相反的函数,对吧?;)

所以这

protocol OnOffRepresentable {
    func isInOnState() -> Bool
    func isInOffState() -> Bool
}

变成这个

protocol OnOffRepresentable {
    var on: Bool { get }
}

而且当然

extension UISwitch: OnOffRepresentable { }

extension UIButton: OnOffRepresentable {
    var on: Bool { return selected }
}

将 OnOffRepresentable 与 UILabel 配对

现在我们不能使用OnOffRepresentableas Keyof aDictionary因为我们的协议必须是Hashable. 那么让我们使用另一种数据结构吧!

let elms: [(OnOffRepresentable, UILabel)] = [
    (UISwitch(), UILabel()),
    (UIButton(), UILabel()),
]

而已。

于 2016-08-20T00:35:42.617 回答