optionals - swift the basic



Faça um dicionário Swift onde a chave é "Type"? (2)

Hope AnyHashable ajuda. Mas apareceu no Xcode 8.0

Você pode fazer algo como:

var info: [AnyHashable : Any]? = nil

https://src-bin.com

Eu estou tentando fazer esse tipo de coisa ..

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

mas eu não posso :)

Tipo não declarado 'Tipo'

No exemplo, CellThing é minha classe base, então A:CellThing , B:CellThing , C:CellThing e assim por diante. A idéia é que eu armazenaria vários AAA, BB, CCCC nos arrays do dicionário.

Como fazer um "Type" (idealmente eu acho, restrito ao CellThing) ser a chave em um dicionário Swift ?

Eu aprecio que eu poderia (talvez?) Usar String(describing: T.self) , mas isso me faria perder o sono.

Aqui está um caso de uso, o código previsto seria algo como isto ...

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

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

Então, algo como ...

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
  }

Answer #1

Infelizmente, atualmente não é possível que os tipos de metatipo estejam em conformidade com os protocolos (consulte essa questão relacionada sobre o assunto) - portanto, CellThing.Type não está, e não pode, atualmente está em conformidade com o Hashable . Isso significa, portanto, que não pode ser usado diretamente como a Key de um Dictionary .

No entanto, você pode criar um wrapper para um metatipo, usando ObjectIdentifier para fornecer a implementação Hashable . Por exemplo:

/// 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 }
}

Você também pode fornecer um subscrito de conveniência no Dictionary que usa um metatipo e o coloca em um HashableType para você:

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

que poderia então usar assim:

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]

Isso também deve funcionar bem para os genéricos, você simplesmente T.self seu dicionário com T.self .

Infelizmente, uma desvantagem de usar um subscript com get e set aqui é que você incorrerá em um acerto de desempenho ao trabalhar com valores de dicionário que são tipos copy-on-write, como Array (como em seu exemplo). Eu falo sobre esta questão mais neste Q & A.

Uma operação simples como:

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

irá disparar uma cópia O (N) da matriz armazenada no dicionário.

Este é um problema que deve ser resolvido com acessores generalizados , que foram implementados como um recurso de linguagem não oficial no Swift 5. Se você estiver confortável usando um recurso de linguagem não oficial que pode quebrar em uma versão futura (não é realmente recomendado para código de produção ), então você poderia implementar o subscrito como:

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

que resolve o problema de desempenho, permitindo que um valor de array seja mutado in-loco no dicionário.

Caso contrário, uma alternativa simples é não definir um subscrito personalizado e, em vez disso, basta adicionar uma propriedade computada de conveniência em seu tipo para permitir que você a use como uma chave:

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]




types