No se admite el uso como tipo concreto conforme al protocolo AnyObject


Estoy usando Swift 2 y usando 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 {

}

Entonces en mi ViewController, declaro

public var delegates = [WeakContainer<MyDelegate>]

Pero es un error

No se admite el uso de MyDelegate como un tipo concreto conforme al protocolo AnyObject

Veo que el error es que WeakContainer tiene value miembro declarado como weak, entonces T se espera que sea objeto. Pero también declaro MyDelegate como AnyObject, también. Cómo moverse por la ciudad ¿esto?

Author: onmyway133, 2015-09-27

6 answers

Tuve la misma idea de crear un contenedor débil con genéricos.
Como resultado, creé wrapper para NSHashTable e hice algunas soluciones para el error del compilador.

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 mejor solución en este momento.

 11
Author: Vlad Papko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-14 05:32:24

Me encontré con el mismo problema cuando traté de implementar contenedores débiles. Como @ plivesey señala en un comentario anterior, esto parece ser un error 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 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 llevar a otros problemas (algunos tipos no se pueden representar en Objective-C), aquí hay un enfoque alternativo:

Solución 2

Suelte el requisito AnyObject del contenedor y envíe 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: Falla guardar valores que se ajustan a T pero no son AnyObject.

 15
Author: Theo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-28 17:35:04

¿Por qué intentas usar genéricos? Yo 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

 2
Author: Kyle Redfearn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-10 03:45:33

Su problema es que WeakContainer requiere que su tipo genérico T sea un subtipo de AnyObject - una declaración protocol no es un subtipo de AnyObject. Tienes cuatro opciones:

  1. En lugar de declarar WeakContainer<MyDelegate> reemplácelo con algo que realmente implemente MyDelegate. El enfoque Swift-y para esto es usar el patrón AnyX : struct AnyMyDelegate : MyDelegate { ... }

  2. Define MyDelegate como 'ligado a clase' como protocol MyDelegate : class { ... }

  3. Anotar MyDelegate con @obj que, esencialmente, lo hace "vinculado a una clase"

  4. Reformular WeakContainer a no requiere su tipo genérico para heredar de AnyObject. Será difícil hacer que esto funcione porque necesita una propiedad declarada como weak var y hay limitaciones en cuanto a qué tipos son aceptados por weak var, que son AnyObject esencialmente.

 1
Author: GoZoner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-28 14:05:41

Si su Protocolo se puede marcar como @obj, entonces 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()

}
 1
Author: Misha Vyrko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-09 08:42:46

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
            }
        }
    }
}
 0
Author: Valentin Shergin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-02 20:29:27