我使用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
。如何解决这个问题?
答案 0 :(得分:16)
当我尝试实现弱容器时遇到了同样的问题。正如@plivesey在上面的评论中指出的那样,这似乎是Swift 2.2 / Xcode 7.3中的bug,但它是expected to work。
但是,某些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
的值会失败。
答案 1 :(得分:12)
我有同样的想法,用泛型创建弱容器
结果,我为- evaluateJavaScript:completionHandler:
创建了包装器,并为编译器错误做了一些解决方法。
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
协议,则应用程序将崩溃。但我现在没有看到任何更好的解决方案。
答案 2 :(得分:3)
为什么要尝试使用泛型?我建议做以下事情:
import Foundation
import UIKit
protocol MyDelegate : AnyObject {
}
class WeakContainer : AnyObject {
weak var value: MyDelegate?
}
class ViewController: UIViewController {
var delegates = [WeakContainer]()
}
还有NSValue
的{{1}}
答案 3 :(得分:2)
如果您的协议可以标记为@obj,那么您可以使用以下代码
protocol Observerable {
associatedtype P : AnyObject
var delegates: NSHashTable<P> { get }
}
@objc protocol MyProtocol {
func someFunc()
}
class SomeClass : Observerable {
var delegates = NSHashTable<MyProtocol>.weakObjects()
}
答案 4 :(得分:1)
您的问题是WeakContainer
要求其通用类型T
是AnyObject
的子类型 - protocol
声明不是子类型AnyObject
。您有四种选择:
而不是声明WeakContainer<MyDelegate>
将其替换为实际实现MyDelegate
的内容。 Swift-y方法是使用AnyX
模式:struct AnyMyDelegate : MyDelegate { ... }
将MyDelegate
定义为&#39;类绑定&#39;作为protocol MyDelegate : class { ... }
使用MyDelegate
对@obj
进行注释,基本上可以使其受到类绑定&#39;
将WeakContainer
改为而非要求其通用类型继承自AnyObject
。您很难完成这项工作,因为您需要一个声明为weak var
的属性,weak var
接受的类型有限制 - 基本上是AnyObject
。
答案 5 :(得分:0)
这是我在纯Swift中的WeakSet实现(没有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
}
}
}
}