web-dev-qa-db-fra.com

L'utilisation comme type concret conforme au protocole AnyObject n'est pas prise en charge

J'utilise Swift 2 et j'utilise WeakContainer comme moyen de stocker un ensemble d'objets faibles, un peu comme NSHashTable.weakObjectsHashTable()

struct WeakContainer<T: AnyObject> {
    weak var value: T?
}

public protocol MyDelegate : AnyObject {

}

Ensuite, dans mon ViewController, je déclare

public var delegates = [WeakContainer<MyDelegate>]

Mais c'est une erreur

Utiliser MyDelegate comme type concret conforme au protocole AnyObject n'est pas pris en charge

Je vois que l'erreur est que WeakContainer a value membre déclaré comme weak, donc T devrait être un objet. Mais je déclare également MyDelegate comme AnyObject, aussi. Comment contourner cela?

33
onmyway133

J'ai eu la même idée de créer un conteneur faible avec des génériques.
En conséquence, j'ai créé un wrapper pour NSHashTable et j'ai fait quelques contournements pour l'erreur de votre compilateur.

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

Usage:

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()
}

Ce n'est pas la meilleure solution, car WeakSet peut être initialisé avec n'importe quel type, et si ce type n'est pas conforme au protocole AnyObject, l'application se bloquera. Mais je ne vois pas de meilleure solution pour le moment.

12
Vlad Papko

J'ai rencontré le même problème lorsque j'ai essayé d'implémenter des conteneurs faibles. Comme @plivesey le fait remarquer dans un commentaire ci-dessus, cela semble être un bug in Swift 2.2/Xcode 7.3, mais il est devrait fonctionner .

Toutefois, le problème ne se produit pas pour certains protocoles Foundation. Par exemple, cela compile:

let container = WeakContainer<NSCacheDelegate>()

J'ai découvert que cela fonctionne pour les protocoles marqués d'un @objc attribut. Vous pouvez l'utiliser comme solution de contournement:

Solution de contournement 1

@objc
public protocol MyDelegate : AnyObject { }

let container = WeakContainer<MyDelegate>() // No compiler error

Comme cela peut entraîner d'autres problèmes (certains types ne peuvent pas être représentés dans Objective-C), voici une approche alternative:

Solution de contournement 2

Supprimez l'exigence AnyObject du conteneur et convertissez la valeur en AnyObject en interne.

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

Avertissement: l'enregistrement des valeurs conformes à T mais qui ne sont pas AnyObjects échoue.

16
Theo

Pourquoi essayez-vous d'utiliser des génériques? Je suggérerais de faire ce qui suit:

import Foundation
import UIKit

protocol MyDelegate : AnyObject {

}

class WeakContainer : AnyObject {
    weak var value: MyDelegate?
}

class ViewController: UIViewController {
    var delegates = [WeakContainer]()
}

Il y a aussi NSValuenonretainedObject

3
Kyle Redfearn

Si votre protocole peut être marqué comme @obj, vous pouvez utiliser le code ci-dessous

protocol Observerable {

    associatedtype P : AnyObject

    var delegates: NSHashTable<P> { get }
}

@objc protocol MyProtocol {

    func someFunc()

}

class SomeClass : Observerable {

    var delegates = NSHashTable<MyProtocol>.weakObjects()

}
2
Misha Vyrko

Votre problème est que WeakContainer requiert que son type générique T soit un sous-type de AnyObject - une déclaration protocoln'est pas a sous-type de AnyObject. Vous avez quatre options:

  1. Au lieu de déclarer WeakContainer<MyDelegate> le remplacer par quelque chose qui implémente réellement MyDelegate. L'approche Swift-y consiste à utiliser le modèle AnyX: struct AnyMyDelegate : MyDelegate { ... }

  2. Définissez MyDelegate comme "lié à la classe" comme protocol MyDelegate : class { ... }

  3. Annoter MyDelegate avec @obj ce qui, essentiellement, le rend "lié à la classe"

  4. Reformuler WeakContainer en pas exiger que son type générique hérite de AnyObject. Vous aurez du mal à faire fonctionner cela car vous avez besoin d'une propriété déclarée comme weak var et il existe des limitations quant aux types acceptés par weak var - qui sont AnyObject essentiellement.

1
GoZoner

Voici mon implémentation de WeakSet en pur Swift (sans 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