2017-02-03 55 views
3

我怎麼能做到這一點?Int和String如何被接受爲AnyHashable?

var dict = [AnyHashable : Int]() 
    dict[NSObject()] = 1 
    dict[""] = 2 

這意味着NSObjectString是不知何故亞型AnyHashableAnyHashable是一個struct那麼,他們是如何允許這樣做?

+0

看看這個,它可能會有所幫助:http://kandelvijaya.com/2016/10/10/swift3-why-anyhashable-how-does-it-work-internally/。我覺得我的回答在這種情況下是無益的,所以我把它刪除了 –

回答

4

請考慮Optional是一個enum,它也是一種值類型 - 但您可以自由地將String轉換爲Optional<String>。答案只不過是編譯器爲你隱式執行這些轉換。

如果我們看一下以下代碼發出的SIL:

let i: AnyHashable = 5 

我們可以看到,編譯器插入到_swift_convertToAnyHashable呼叫:在AnyHashable.swift

// allocate memory to store i, and get the address. 
    alloc_global @main.i : Swift.AnyHashable, loc "main.swift":9:5, scope 1 // id: %2 
    %3 = global_addr @main.i : Swift.AnyHashable : $*AnyHashable, loc "main.swift":9:5, scope 1 // user: %9 

    // allocate temporary storage for the Int, and intialise it to 5. 
    %4 = alloc_stack $Int, loc "main.swift":9:22, scope 1 // users: %7, %10, %9 
    %5 = integer_literal $Builtin.Int64, 5, loc "main.swift":9:22, scope 1 // user: %6 
    %6 = struct $Int (%5 : $Builtin.Int64), loc "main.swift":9:22, scope 1 // user: %7 
    store %6 to %4 : $*Int, loc "main.swift":9:22, scope 1 // id: %7 

    // call _swift_convertToAnyHashable, passing in the address of i to store the result, and the address of the temporary storage for the Int. 
    // function_ref _swift_convertToAnyHashable 
%8 = function_ref @_swift_convertToAnyHashable : [email protected](thin) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0) -> @out AnyHashable, loc "main.swift":9:22, scope 1 // user: %9 
    %9 = apply %8<Int>(%3, %4) : [email protected](thin) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0) -> @out AnyHashable, loc "main.swift":9:22, scope 1 

    // deallocate temporary storage. 
    dealloc_stack %4 : $*Int, loc "main.swift":9:22, scope 1 // id: %10

來看,我們可以看到函數的名稱爲_swift_convertToAnyHashable,它只是簡單地調用AnyHashable's initialiser

@_silgen_name("_swift_convertToAnyHashable") 
public // COMPILER_INTRINSIC 
func _convertToAnyHashable<H : Hashable>(_ value: H) -> AnyHashable { 
    return AnyHashable(value) 
} 

因此,上述代碼僅僅是等效於:

let i = AnyHashable(5) 

雖然這是奇怪的是標準庫also implements an extensionDictionary(其@OOPer shows),允許一個字典類型的KeyAnyHashable將被任何_Hashable符合類型(我不相信有任何類型符合_Hashable,但不符合Hashable)。

對於_Hashable鍵,下標本身應該正常工作,沒有特殊的過載。而不是默認的下標(這將需要一個AnyHashable密鑰)可以只將與上述隱式轉換所使用的,如下面的示例示出了:

struct Foo { 
    subscript(hashable: AnyHashable) -> Any { 
     return hashable.base 
    } 
} 

let f = Foo() 
print(f["yo"]) // yo 

編輯:在夫特4,前述下標過載和_Hashable既已經從STDLIB通過this commit除去與描述:

我們有一個隱式轉換到AnyHashable,所以沒有 需要對詞典的特殊標都沒有。

這證實了我的懷疑。

+0

換句話說,它來自Swift團隊的純粹魔力......感謝洞察力! – Remover

+0

@Remover樂於助人:)你會在語言中找到不少例子,爲什麼他們可能的答案只是「編譯器魔術」。另一個值得關注的問題是數組從隱式轉換到超類型元素,由於泛型的不變性,這是不可能的,但編譯器會在幕後執行轉換(參見例如[this Q&A](http://stackoverflow.com/q/37188580/2976878))。 – Hamish

-1
class SomeClass: NSObject { 
    var something: String = "something" 
} 

var dict = [AnyHashable: Int]() 
var object = SomeClass() 

dict = ["a": 1, object: 2] 

print(dict["a"]) // result: Optional(1) 
print(dict[object]) // result: Optional(2) 

var object2 = SomeClass() 
dict[object2] = 3 
print(dict[object2]) // result: Optional(3) 
1

你可以找到這個代碼,當你的dict[NSObject()] = 1[]在Xcode的斯威夫特編輯器(在8.3測試版8.2.1,有少許差別)CMD-點擊:

extension Dictionary where Key : _AnyHashableProtocol { 

    public subscript(key: _Hashable) -> Value? 

    public mutating func updateValue<ConcreteKey : Hashable>(_ value: Value, forKey key: ConcreteKey) -> Value? 

    public mutating func removeValue<ConcreteKey : Hashable>(forKey key: ConcreteKey) -> Value? 
} 

_AnyHashableProtocol_Hashable是隱藏類型,因此您可能需要檢查Swift source code。簡單地說:

  • _HashableHashable隱藏超協議,因此,IntStringNSObject或所有其它類型的Hashable符合_Hashable

  • _AnyHashableProtocol是一個隱藏的協議,其中AnyHashable是一個符合_AnyHashableProtocol的唯一類型。

所以,上面的擴展與this code in Swift 3.1非常相似。

extension Dictionary where Key == AnyHashable { 

    //... 
} 

你看,當你寫這樣的代碼是這樣的:

var dict = [AnyHashable : Int]() 
    dict[NSObject()] = 1 
    dict[""] = 2 

您正在使用的擴展定義的subscript操作。

+0

哇......有趣...看起來有很多東西要學Swift – Remover

+0

@Remover,那應該是我的話。重新檢查[SE-0131](https://github.com/apple/swift-evolution/blob/master/proposals/0131-anyhashable.md)沒有說什麼。在非優化生成的代碼中,Swift使用'dict'的'subscript'擴展版本,並且在優化後的代碼中稍微複雜一些...鼓掌Hamish。 – OOPer

相關問題