swift - Utilizando como tipo concreto conforme al protocolo AnyObject no es compatible
reference hashtable (6)
Estoy usando Swift 2 y el uso de WeakContainer como una forma de almacenar un conjunto de objetos débiles, al igual que NSHashTable.weakObjectsHashTable()
struct WeakContainer<T: AnyObject> {
weak var value: T?
}
public protocol MyDelegate : AnyObject {
}
Luego, en mi ViewController, declaro
public var delegates = [WeakContainer<MyDelegate>]
Pero es un error
Usando MyDelegate como un tipo concreto conforme al protocolo AnyObject no es compatible
Veo que el error es que WeakContainer
tiene un miembro de value
declarado como weak
, por lo que se espera que T
sea objeto. Pero también declaro MyDelegate
como AnyObject
también. Cómo evitar esto?
¿Por qué estás tratando de usar genéricos? Sugeriría hacer lo siguiente:
import Foundation
import UIKit
protocol MyDelegate : AnyObject {
}
class WeakContainer : AnyObject {
weak var value: MyDelegate?
}
class ViewController: UIViewController {
var delegates = [WeakContainer]()
}
También hay NSValue
''s nonretainedObject
Aquí está mi implementación de WeakSet en Swift puro (sin NSHashTable).
internal struct WeakBox<T: AnyObject> {
internal private(set) weak var value: T?
private var pointer: UnsafePointer<Void>
internal init(_ value: T) {
self.value = value
self.pointer = unsafeAddressOf(value)
}
}
extension WeakBox: Hashable {
var hashValue: Int {
return self.pointer.hashValue
}
}
extension WeakBox: Equatable {}
func ==<T>(lhs: WeakBox<T>, rhs: WeakBox<T>) -> Bool {
return lhs.pointer == rhs.pointer
}
public struct WeakSet<Element>: SequenceType {
private var boxes = Set<WeakBox<AnyObject>>()
public mutating func insert(member: Element) {
guard let object = member as? AnyObject else {
fatalError("WeakSet''s member (/(member)) must conform to AnyObject protocol.")
}
self.boxes.insert(WeakBox(object))
}
public mutating func remove(member: Element) {
guard let object = member as? AnyObject else {
fatalError("WeakSet''s member (/(member)) must conform to AnyObject protocol.")
}
self.boxes.remove(WeakBox(object))
}
public mutating func removeAll() {
self.boxes.removeAll()
}
public func contains(member: Element) -> Bool {
guard let object = member as? AnyObject else {
fatalError("WeakSet''s member (/(member)) must conform to AnyObject protocol.")
}
return self.boxes.contains(WeakBox(object))
}
public func generate() -> AnyGenerator<Element> {
var generator = self.boxes.generate()
return AnyGenerator {
while(true) {
guard let box = generator.next() else {
return nil
}
guard let element = box.value else {
continue
}
return element as? Element
}
}
}
}
Me encontré con el mismo problema cuando traté de implementar contenedores débiles. Como señala @plivesey en un comentario anterior, esto parece ser un bugs.swift.org/browse/SR-1176 en Swift 2.2 / Xcode 7.3, pero se espera que funcione .
Sin embargo, el problema no ocurre para algunos protocolos de Foundation. Por ejemplo, esto compila:
let container = WeakContainer<NSCacheDelegate>()
Descubrí que esto funciona para los protocolos marcados con el atributo @objc
. Puede usar esto como una solución alternativa:
Solución 1
@objc
public protocol MyDelegate : AnyObject { }
let container = WeakContainer<MyDelegate>() // No compiler error
Como esto puede conducir a otros problemas (algunos tipos no se pueden representar en Objective-C), aquí hay un enfoque alternativo:
Solución 2
AnyObject
requisito de AnyObject
del contenedor y AnyObject
el valor a AnyObject
internamente.
struct WeakContainer<T> {
private weak var _value:AnyObject?
var value: T? {
get {
return _value as? T
}
set {
_value = newValue as? AnyObject
}
}
}
protocol MyDelegate : AnyObject { }
var container = WeakContainer<MyDelegate>() // No compiler error
Advertencia: Guardar valores que se ajustan a T
pero no son AnyObject
s falla.
Si su protocolo puede marcarse como @obj, puede usar el código a continuación
protocol Observerable {
associatedtype P : AnyObject
var delegates: NSHashTable<P> { get }
}
@objc protocol MyProtocol {
func someFunc()
}
class SomeClass : Observerable {
var delegates = NSHashTable<MyProtocol>.weakObjects()
}
Su problema es que WeakContainer
requiere que su tipo genérico T
sea un subtipo de AnyObject
; una declaración de protocol
no es un subtipo de AnyObject
. Tienes cuatro opciones:
En lugar de declarar a
WeakContainer<MyDelegate>
reemplazarlo con algo que realmente implementeMyDelegate
. El enfoque de Swift-y para esto es usar el patrónAnyX
:struct AnyMyDelegate : MyDelegate { ... }
Defina
MyDelegate
como ''clase enlazada'' comoprotocol MyDelegate : class { ... }
MyDelegate
con@obj
que, esencialmente, lo hace ''clase enlazado''Reformular
WeakContainer
para no requerir que su tipo genérico herede deAnyObject
. Te será difícil hacer esto porque necesitas una propiedad declarada comoweak var
y existen limitaciones en cuanto a qué tipos aceptan lasAnyObject
weak var
, que esencialmente sonAnyObject
.
Tuve la misma idea de crear contenedores débiles con genéricos.
Como resultado, creé el contenedor para NSHashTable
e hice algunas soluciones para tu error de compilación.
class WeakSet<ObjectType>: SequenceType {
var count: Int {
return weakStorage.count
}
private let weakStorage = NSHashTable.weakObjectsHashTable()
func addObject(object: ObjectType) {
guard object is AnyObject else { fatalError("Object (/(object)) should be subclass of AnyObject") }
weakStorage.addObject(object as? AnyObject)
}
func removeObject(object: ObjectType) {
guard object is AnyObject else { fatalError("Object (/(object)) should be subclass of AnyObject") }
weakStorage.removeObject(object as? AnyObject)
}
func removeAllObjects() {
weakStorage.removeAllObjects()
}
func containsObject(object: ObjectType) -> Bool {
guard object is AnyObject else { fatalError("Object (/(object)) should be subclass of AnyObject") }
return weakStorage.containsObject(object as? AnyObject)
}
func generate() -> AnyGenerator<ObjectType> {
let enumerator = weakStorage.objectEnumerator()
return anyGenerator {
return enumerator.nextObject() as! ObjectType?
}
}
}
Uso:
protocol MyDelegate : AnyObject {
func doWork()
}
class MyClass: AnyObject, MyDelegate {
fun doWork() {
// Do delegated work.
}
}
var delegates = WeakSet<MyDelegate>()
delegates.addObject(MyClass())
for delegate in delegates {
delegate.doWork()
}
No es la mejor solución, porque WeakSet
se puede inicializar con cualquier tipo, y si este tipo no se ajusta al protocolo AnyObject
, la aplicación se bloqueará. Pero no veo ninguna solución mejor en este momento.