![](/img/trans.png)
[英]How can I cast a an array of protocol X to `Hashable` if the class conform to X is Hashable?
[英]Creating a protocol that represents hashable objects that can be on or off
我正在嘗試創建一個簡單的協議,說明對象是處於“開啟”狀態還是“關閉”狀態。 對此的解釋取決於實施對象。 對於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
好的...所以我做了一些堆棧溢出挖掘和搜索。 我發現很多文章似乎很有希望,比如Swift中的Set和protocol , 使用某些協議作為符合其他協議的具體類型是不受支持的 ,我看到有一些關於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
協議的類,但我無法弄清楚如何使它全部連接。
我不知道我是否必須使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
使用associatedType
創建協議(或使其符合另一個具有像Hashable
這樣的associatedType
Hashable
協議)將使該協議與泛型Hashable
。
我建議你一個非常簡單的解決方法
首先,我們不需要2個完全相反的功能? ;)
所以這
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
作為Dictionary
Key
,因為我們的協議必須是Hashable
。 然后讓我們使用另一種數據結構!
let elms: [(OnOffRepresentable, UILabel)] = [
(UISwitch(), UILabel()),
(UIButton(), UILabel()),
]
而已。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.