2017-02-03 84 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

相关问题