【问题标题】:Make a Swift dictionary where the key is "Type"?制作一个键为“类型”的 Swift 字典?
【发布时间】:2017-07-16 12:22:56
【问题描述】:

我正在尝试做这种事情..

static var recycle: [Type: [CellThing]] = []

但是 - 我不能:)

未声明的类型“类型”

在示例中,CellThing 是我的基类,所以 A:CellThingB:CellThingC:CellThing 等等。我的想法是我会在字典数组中存储各种 A A A、B B、C C C C。

如何使“类型”(理想情况下我猜,仅限于 CellThing)成为 Swift 字典中的键

我很感激我可以(也许?)使用String(describing: T.self),但这会让我失眠。


这是一个用例,设想的代码看起来像这样......

@discardableResult class func make(...)->Self {
  return makeHelper(...)
  }

private class func makeHelper<T: CellThing>(...)->T {
  let c = instantiateViewController(...) as! T
  return c
  }

那么像...

static var recycle: [Type: [CellThing]] = []

private class func makeHelper<T: CellThing>(...)->T {
  let c = instantiateViewController(...) as! T

  let t = type whatever of c (so, maybe "A" or "B")
  recycle[t].append( c )

  let k = recycle[t].count
  print wow, you have k of those already!

  return c
  }

【问题讨论】:

  • 您尝试过 CellThing.self
  • 类型可以散列吗?
  • @matt - 我很感激我可能必须使 CellThing 可散列。这真的是问题吗(而且错误只是奇怪的)??
  • 是的。它正在寻找一个名为 Type 的类,但没有。我可以使用类作为非静态变量的键: var recycle: [UIView.self: [String]] = [] // 没有编译器错误
  • 我也不知道AnyHashable 会有什么帮助;它只能包装Hashable 的东西,但元类型不是Hashable

标签: swift dictionary types


【解决方案1】:

不幸的是,元类型目前不可能符合协议(请参阅this related question 关于此事) - 所以CellThing.Type 目前不符合Hashable。因此,这意味着它不能直接用作DictionaryKey

但是,您可以使用ObjectIdentifier 为元类型创建包装器,以提供Hashable 实现。例如:

/// Hashable wrapper for a metatype value.
struct HashableType<T> : Hashable {

  static func == (lhs: HashableType, rhs: HashableType) -> Bool {
    return lhs.base == rhs.base
  }

  let base: T.Type

  init(_ base: T.Type) {
    self.base = base
  }

  func hash(into hasher: inout Hasher) {
    hasher.combine(ObjectIdentifier(base))
  }
  // Pre Swift 4.2:
  // var hashValue: Int { return ObjectIdentifier(base).hashValue }
}

然后,您还可以在 Dictionary 上提供一个方便的下标,它接受一个元类型并将其包装在 HashableType 中:

extension Dictionary {
  subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
    get { return self[HashableType(key)] }
    set { self[HashableType(key)] = newValue }
  }
}

然后可以这样使用:

class CellThing {}
class A : CellThing {}
class B : CellThing {}

var recycle: [HashableType<CellThing>: [CellThing]] = [:]

recycle[A.self] = [A(), A(), A()]
recycle[B.self] = [B(), B()]

print(recycle[A.self]!) // [A, A, A]
print(recycle[B.self]!) // [B, B]

这也应该适用于泛型,您只需使用 T.self 为您的字典下标即可。


不幸的是,在此处使用带有getset 的下标的一个缺点是,在使用诸如Array 之类的写时复制类型的字典值(例如在你的例子中)。这个问题我多讲in this Q&A.

一个简单的操作如:

recycle[A.self]?.append(A())

将触发存储在字典中的数组的 O(N) 副本。

这是一个旨在通过 generalised accessors 解决的问题,它已作为 Swift 5 中的 非官方 语言功能实现。如果您愿意使用可能会破坏的非官方语言功能在未来的版本中(不推荐用于生产代码),那么您可以将下标实现为:

extension Dictionary {
  subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
    get { return self[HashableType(key)] }
    _modify {
      yield &self[HashableType(key)]
    }
  }
}

解决了性能问题,允许在字典中就地改变数组值。

否则,一个简单的替代方法是不定义自定义下标,而是在您的类型上添加一个方便的计算属性,让您将其用作键:

class CellThing {
  // Convenience static computed property to get the wrapped metatype value.
  static var hashable: HashableType<CellThing> { return HashableType(self) }
}

class A : CellThing {}
class B : CellThing {}

var recycle: [HashableType<CellThing>: [CellThing]] = [:]

recycle[A.hashable] = [A(), A(), A()]
recycle[B.hashable] = [B(), B()]

print(recycle[A.hashable]!) // [A, A, A]
print(recycle[B.hashable]!) // [B, B]

【讨论】:

  • 所以回顾一下,看看问题标题 (1) 实际上存在的元类型是:CellThing.Type (2) 但是,实际上在 Swift 中 你不能 使元类型可散列:就是这样。谁知道?
  • @JoeBlow CellThing.Type 是元类型 type,它是类型的类型(而 CellThing.self 是元类型 value,一个的例子)。您目前不能直接将元类型类型与协议一致。有关此事,请参阅this related question
  • @matt 元类型值只是指向给定类型的底层元类型信息的指针——ObjectIdentifier 只是使用这些指针值(你可以看到in the source 它只是转换为Builtin.RawPointer ) 为您提供hashValue 实现(以及通过简单的身份比较实现的平等)。
  • 我很抱歉碰到这么一个旧线程,但我只是想知道,如果CellThing 不是基类而是协议,有没有办法让你的答案工作?当我尝试它时,Swift 告诉我 cannot subscript a value of type '[HashableType&lt;CellThing&gt; : [CellThing]]' with an argument of type 'A.Type' print(recycle[A.self]!)
  • @Evert 感谢您的提问。我有同样的问题。你找到解决办法了吗?
【解决方案2】:

如果你扩展Dictionary type,你可以直接使用已经定义的泛型Key

extension Dictionary {
    // Key and Value are already defined by type dictionary, so it's available here
    func getSomething(key: Key) -> Value {
        return self[key]
    }
}

这是因为 Dictionary 已经定义了泛型 KeyValue 供自己使用。

【讨论】:

  • 七,谢谢-我..不确定这是否能解决问题,因为我不再理解我写的问题:) 必须考虑一下。并问 Hamish :)
  • @Fattie - 我读到的问题是我如何编写一些需要泛型并在字典上操作的东西。最简单的方法(至少对我来说)就是扩展字典。
  • @sevensevens 不完全——问题是如何使用元类型作为字典键,例如你将如何编写类似[String.self: "hello"]dict[String.self] = "hello" 的东西。话虽这么说,使用字典扩展的想法让我知道如何定义一个方便的下标来处理在HashableType 中为你装箱的元类型:)
【解决方案3】:

希望AnyHashable 有所帮助。但它出现在 Xcode 8.0

你可以这样做:

var info: [AnyHashable : Any]? = nil

【讨论】:

  • 仍然,Type 不是 Hashable
猜你喜欢
  • 2016-09-26
  • 1970-01-01
  • 2015-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多