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?
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.
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 AnyObject
s échoue.
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 NSValue
nonretainedObject
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()
}
Votre problème est que WeakContainer
requiert que son type générique T
soit un sous-type de AnyObject
- une déclaration protocol
n'est pas a sous-type de AnyObject
. Vous avez quatre options:
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 { ... }
Définissez MyDelegate
comme "lié à la classe" comme protocol MyDelegate : class { ... }
Annoter MyDelegate
avec @obj
ce qui, essentiellement, le rend "lié à la classe"
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.
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
}
}
}
}