Использование в качестве конкретного типа, соответствующего протоколу AnyObject, не поддерживается
Я использую Swift 2 и использую WeakContainer как способ хранения набора слабых объектов, как NSHashTable.weakObjectsHashTable()
struct WeakContainer<T: AnyObject> {
weak var value: T?
}
public protocol MyDelegate : AnyObject {
}
Затем в моем ViewController я объявляю
public var delegates = [WeakContainer<MyDelegate>]
Но это ошибка
Использование MyDelegate в качестве конкретного типа, соответствующего протоколу AnyObject, не поддерживается
Я вижу, что ошибка состоит в том, что WeakContainer
имеет член value
, объявленный как weak
, поэтому ожидается, что T
будет объектом. Но я также объявляю MyDelegate
как AnyObject
. Как обойти это?
Ответы
Ответ 1
У меня была та же идея создать слабый контейнер с дженериками.
В результате я создал обертку для NSHashTable
и сделал некоторое обходное решение для вашей ошибки компилятора.
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?
}
}
}
Использование:
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()
}
Это не лучшее решение, потому что WeakSet
может быть инициализировано любым типом, и если этот тип не соответствует протоколу AnyObject
, приложение будет аварийно завершено. Но сейчас я не вижу лучшего решения.
Ответ 2
Я столкнулся с той же проблемой, когда попытался реализовать слабые контейнеры. Как отмечает @plivesey в комментарии выше, это выглядит как bug в Swift 2.2/Xcode 7.3, но это ожидается работа.
Однако проблема не возникает для некоторых протоколов Foundation. Например, это компилируется:
let container = WeakContainer<NSCacheDelegate>()
Я узнал, что это работает для протоколов, отмеченных атрибутом @objc
. Вы можете использовать это как обходной путь:
Обходной путь 1
@objc
public protocol MyDelegate : AnyObject { }
let container = WeakContainer<MyDelegate>() // No compiler error
Так как это может привести к другим проблемам (некоторые типы не могут быть представлены в Objective-C), вот альтернативный подход:
Обходной путь 2
Отбросьте требование AnyObject
из контейнера и оставьте значение AnyObject
внутренне.
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
Предостережение: сохраняются значения, соответствующие T
, но не AnyObject
.
Ответ 3
Почему вы пытаетесь использовать дженерики? Я бы предложил сделать следующее:
import Foundation
import UIKit
protocol MyDelegate : AnyObject {
}
class WeakContainer : AnyObject {
weak var value: MyDelegate?
}
class ViewController: UIViewController {
var delegates = [WeakContainer]()
}
Существует также NSValue
nonretainedObject
Ответ 4
Ваша проблема в том, что WeakContainer
требует, чтобы его общий тип T
был подтипом AnyObject
- объявление protocol
не является подтипом AnyObject
. У вас есть четыре варианта:
-
Вместо объявления WeakContainer<MyDelegate>
замените его на что-то, что на самом деле реализует MyDelegate
. Подход Swift-y для этого состоит в использовании шаблона AnyX
: struct AnyMyDelegate : MyDelegate { ... }
-
Определите MyDelegate
как "связанный с классом" как protocol MyDelegate : class { ... }
-
Аннотировать MyDelegate
с помощью @obj
, который, по сути, делает его "связанным с классом"
-
Измените WeakContainer
, чтобы не требовать, чтобы его общий тип наследовал от AnyObject
. Вам будет трудно выполнить эту работу, потому что вам нужно свойство, объявленное как weak var
, и есть ограничение относительно того, какие типы принимаются weak var
- которые AnyObject
существенно.
Ответ 5
Если ваш протокол может быть помечен как @obj, вы можете использовать код ниже
protocol Observerable {
associatedtype P : AnyObject
var delegates: NSHashTable<P> { get }
}
@objc protocol MyProtocol {
func someFunc()
}
class SomeClass : Observerable {
var delegates = NSHashTable<MyProtocol>.weakObjects()
}
Ответ 6
Вот моя реализация WeakSet в чистом Swift (без 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 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 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 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
}
}
}
}