使用作为符合协议的任何对象的具体类型是不支持的。

37

我正在使用Swift 2,并使用WeakContainer作为一种存储弱对象集合的方式,类似于NSHashTable.weakObjectsHashTable()

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

public protocol MyDelegate : AnyObject {

}

然后在我的视图控制器中,我声明

public var delegates = [WeakContainer<MyDelegate>]

但出现了错误

使用 MyDelegate 作为符合协议 AnyObject 的具体类型是不被支持的

我看到这个错误是因为 WeakContainer 声明了 value 成员为 weak,所以期望 T 是一个对象。但我也将 MyDelegate 声明为 AnyObject。如何解决这个问题?


@nhgrif 我之前尝试过 public protocol MyDelegate : class,但是不起作用。 - onmyway133
@nhgrif 在这个问题中遇到了相同的错误。NSHashTable 不是泛型,我不想要那个。 - onmyway133
1
http://i.imgur.com/B0swn6w.png - nhgrif
1
有人已经想出了解决方案吗?我也遇到了完全相同的问题... - Yeehaw
4
还没确定。这里是描述同一问题的 Jira 票:https://bugs.swift.org/browse/SR-1176 - plivesey
显示剩余7条评论
6个回答

18
我在尝试实现弱容器时遇到了同样的问题。正如@plivesey在上面的评论中指出的那样,这似乎是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 的值将会失败。

2
解决方法2在这里很好用,尽管当然不应该需要!做得好。 - HughHughTeotl
2
解决方法2让我避免了一次大规模的重写。谢谢@Theo。 - Clay Ellis
1
酷哥!你是怎么发现Workaround2的?它真的很好用。还有Workaround1就像魔法一样! - Pandara
_value = newValue as? AnyObject 这一行出现了警告 Conditional cast from 'T?' to 'AnyObject' always succeeds。我们该怎么办? - meaning-matters

11

我有一个相同的想法,就是使用泛型创建一个弱引用容器。
结果,我为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协议,则应用程序将崩溃。但是我目前没有看到更好的解决方案。


对象是 AnyObject 的判断总是为真。 - Roman Aliyev

5

你为什么想要使用泛型呢?我建议你按照以下步骤操作:

import Foundation
import UIKit

protocol MyDelegate : AnyObject {

}

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

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

还有NSValuenonretainedObject


4
您建议的 WeakContainer 只能存储类型为 MyDelegate 的值。问题是如何实现此容器,以便它也可以重用于其他协议。 - Theo

3
如果您的协议可以标记为@obj,则可以使用以下代码。
protocol Observerable {

    associatedtype P : AnyObject

    var delegates: NSHashTable<P> { get }
}

@objc protocol MyProtocol {

    func someFunc()

}

class SomeClass : Observerable {

    var delegates = NSHashTable<MyProtocol>.weakObjects()

}

1
你的问题在于WeakContainer要求其泛型类型TAnyObject的子类型 - protocol声明不是AnyObject的子类型。你有四个选项:
  1. 不要声明 WeakContainer<MyDelegate>,而是用实现了 MyDelegate 的东西来替换它。这种情况下,Swift 的做法是使用 AnyX 模式: struct AnyMyDelegate : MyDelegate { ... }

  2. MyDelegate 定义为“类绑定”,即 protocol MyDelegate : class { ... }

  3. @obj 注释 MyDelegate,从本质上讲,使其成为“类绑定”

  4. 重新制定 WeakContainer,使其不需要其泛型类型继承自 AnyObject。你很难让这个工作起来,因为你需要声明一个属性为 weak var,而对于哪些类型被 weak var 接受存在限制 - 基本上是 AnyObject


2
关于选项2:在这里,class是否与AnyObject具有相同的效果?当我尝试时似乎没有任何区别。关于选项4:据我所理解,从AnyObject继承是使泛型类型类绑定的唯一方法。您能提供选项2和选项4的示例实现吗? - Theo

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

这段代码无法在Swift 3中编译。 - Tankista

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接