Int和String如何被接受为AnyHashable?

时间:2017-02-03 09:43:43

标签: swift swift3

我怎么能这样做?

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

这意味着NSObjectString在某种程度上是AnyHashable子类型,但AnyHashablestruct所以,如何他们允许这个吗?

3 个答案:

答案 0 :(得分:5)

考虑Optionalenum,它也是一种值类型 - 但您可以自由地将String转换为Optional<String>。答案很简单,编译器会隐式为您执行这些转换。

如果我们查看以下代码发出的SIL:

let i: AnyHashable = 5

我们可以看到编译器插入了对_swift_convertToAnyHashable的调用:

  // 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 : $@convention(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) : $@convention(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

查看AnyHashable.swift,我们可以看到名称为_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)

虽然对Dictionaryalso implements an extension)的标准库@OOPer shows感到好奇,但允许Key类型AnyHashable的字典为由任何_Hashable符合类型订阅(我不认为有任何类型符合_Hashable,但不符合Hashable)。

下标本身应该可以正常工作而不会导致_Hashable个密钥过载。相反,默认的下标(可以使用AnyHashable键)可以与上面的隐式转换一起使用,如下例所示:

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

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

修改:在Swift 4中,前面提到的下标重载和_Hashable都已通过this commit从stdlib中移除,其描述如下:

  

我们有一个隐式转换为AnyHashable,所以没有   需要在Dictionary上有特殊的下标。

这证实了我的怀疑。

答案 1 :(得分:1)

当你在Xcode的Swift编辑器中点击[] dict[NSObject()] = 1时,你可以找到这段代码(8.2.1,在8.3 beta中略有不同) :

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。简单地:

  • _Hashable是隐藏的Hashable超级协议,因此,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运算符。

答案 2 :(得分:-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)