不支持用作符合协议 AnyObject 的具体类型

Posted

技术标签:

【中文标题】不支持用作符合协议 AnyObject 的具体类型【英文标题】:Using as a concrete type conforming to protocol AnyObject is not supported 【发布时间】:2015-12-24 19:22:23 【问题描述】:

我正在使用 Swift 2 并使用 Wea​​kContainer 作为存储一组弱对象的一种方式,很像 NSHashTable.weakObjectsHashTable()

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


public protocol MyDelegate : AnyObject 


然后在我的 ViewController 中,我声明

public var delegates = [WeakContainer<MyDelegate>]

但这是错误

不支持将 MyDelegate 用作符合协议 AnyObject 的具体类型

我看到错误是WeakContainervalue 成员声明为weak,因此T 应该是对象。但我也将MyDelegate 声明为AnyObject。如何解决这个问题?

【问题讨论】:

错误到底在哪里? NSHashTable 有什么问题? 在您的协议声明中,如果您将AnyObject 更改为class,它应该可以正常工作。不过不要让我解释其中的区别。 i.imgur.com/B0swn6w.png 有人提出解决方案了吗?遭受完全相同的问题... 还没有修复。这是描述相同问题的 jira 票证:bugs.swift.org/browse/SR-1176 【参考方案1】:

我在尝试实现弱容器时遇到了同样的问题。正如@plivesey 在上面的评论中指出的那样,这似乎是 Swift 2.2 / Xcode 7.3 中的 bug,但它是 expected to work。

但是,某些基金会协议不会出现此问题。例如,这样编译:

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 使我免于大规模重写。谢谢@Theo 帅哥!您是如何发现 Workaround2 的?它工作得很好。而且 Workaround1 就像魔术一样! 在线_value = newValue as? AnyObject 上获得警告Conditional cast from 'T?' to 'AnyObject' always succeeds。我们应该怎么做?【参考方案2】:

我也有同样的想法,用泛型创建弱容器。 结果,我为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协议,那么app就会崩溃。但我目前没有看到任何更好的解决方案。

【讨论】:

object is AnyObject 始终为真。【参考方案3】:

您为什么要尝试使用泛型?我建议执行以下操作:

import Foundation
import UIKit

protocol MyDelegate : AnyObject 



class WeakContainer : AnyObject 
    weak var value: MyDelegate?


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

还有NSValuenonretainedObject

【讨论】:

您建议的WeakContainer 只能存储MyDelegate 类型的值。问题是如何实现这个容器,以便它也可以被其他协议重用。【参考方案4】:

如果您的协议可以标记为@obj,那么您可以使用下面的代码

protocol Observerable 

    associatedtype P : AnyObject

    var delegates: NSHashTable<P>  get 


@objc protocol MyProtocol 

    func someFunc()



class SomeClass : Observerable 

    var delegates = NSHashTable<MyProtocol>.weakObjects()


【讨论】:

【参考方案5】:

您的问题是 WeakContainer 要求其泛型类型 TAnyObject 的子类型 - protocol 声明 不是 AnyObject 的子类型。您有四个选择:

    不要声明WeakContainer&lt;MyDelegate&gt;,而是用实际实现MyDelegate 的东西替换它。 Swift-y 的方法是使用 AnyX 模式:struct AnyMyDelegate : MyDelegate ...

    MyDelegate 定义为“类绑定”为protocol MyDelegate : class ...

    @obj 注释MyDelegate,这基本上使它成为“类绑定”

    WeakContainer 重新表述为要求其泛型类型从AnyObject 继承。您将很难完成这项工作,因为您需要一个声明为 weak var 的属性,并且 weak var 接受的类型存在限制 - 本质上是 AnyObject

【讨论】:

关于选项2:这里classAnyObject的效果不一样吗?当我尝试它时,它似乎没有任何区别。至于选项 4:据我了解,从 AnyObject 继承是使泛型类型绑定类的唯一方法。您能否提供选项 2 和 4 的示例实现?【参考方案6】:

这是我在纯 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 编译

以上是关于不支持用作符合协议 AnyObject 的具体类型的主要内容,如果未能解决你的问题,请参考以下文章

类型'[String,AnyObject?]'不符合协议AnyObject?:为什么?

类型 [String: String] 不符合协议 'AnyObject'

类型“AnyObject”不符合协议“NSFetchRequestResult”

类型“NSFastEnumerationIterator.Element”(又名“Any”)不符合协议“AnyObject”

type() 不符合协议 anyobject

NSFastEnumerationIterator.Element(又名 Any)不符合协议“AnyObject”