ios - 创建一个表示可以打开或关闭的可散列对象的协议(protocol)

标签 ios swift protocols type-erasure hashable

我正在尝试创建一个简单的协议(protocol)来说明对象是处于“打开”状态还是“关闭”状态。对那是什么的解释取决于实现对象。对于 UISwitch,它是开关是打开还是关闭(duh)。对于 UIButton,可能是按钮是否处于 selected 状态。对于 Car,它可能是汽车的引擎是否打开,甚至是它是否在移动。所以我着手创建这个简单的协议(protocol):

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'

哦!正确的!傻我。好的,让我使用协议(protocol)组合更新协议(protocol)(毕竟,我想在这里使用的控件都是 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 的好文章,它们似乎正是我需要的:http://krakendev.io/blog/generic-protocols-and-their-shortcomings , http://robnapier.net/erasure , 和 https://realm.io/news/type-erased-wrappers-in-swift/仅举几例。

不过这是我卡住的地方。我已经尝试通读所有这些内容,并尝试创建一个 Hashable 类并且还符合我的 OnOffRepresentable 协议(protocol),但我想不通了解如何将它们连接起来。

最佳答案

我不知道我是否必须使 OnOffRepresentable 协议(protocol)继承自 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

关于ios - 创建一个表示可以打开或关闭的可散列对象的协议(protocol),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39048963/

相关文章:

ios - 如何在 Objective-C 中将 PHAsset 转换为 UIImage

xcode - 是否可以在 Xcode 中禁用 Swift 文档?

Swift Scheduler 滞后?

objective-c - Objective-C 中的抽象类

javascript - 什么是hm ://protocol?

ios - ios json解析成字典

ios - 由 ScrollView 裁剪的阴影

ios - 使用 AVCaptureSession 以编程方式捕获最高分辨率图像

我们可以在 BG96 LwM2M 实现中添加自定义对象吗?

ios - watchKit 的 Storyboard布局显示问题