Использование в качестве конкретного типа, соответствующего протоколу 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
            }
        }
    }
}