【问题标题】:Using protocol as a concrete type conforming to 'AnyObject' is not supported不支持将协议用作符合“AnyObject”的具体类型
【发布时间】:2017-02-08 07:54:25
【问题描述】:

我正在尝试实现一个简单的多代表情况:

protocol Subscribable: class {
    associatedtype Subscriber: AnyObject
    var subscribers: NSHashTable<Subscriber> { get }
}

protocol ControllerSubscriber: class {
    func controllerDidSomething()
}

class Controller: Subscribable {
    typealias Subscriber = ControllerSubscriber
    var subscribers = NSHashTable<Subscriber>.weakObjects()  // Error
}

错误:不支持将“ControllerSubscriber”用作符合协议“AnyObject”的具体类型。

我的问题是:

  • 这个错误到底是什么意思?
  • 我尝试做的事情失败的基本概念是什么?
  • 为什么“不支持”?

当然,我该如何解决这个问题?从实际解决方案的意义上说,不是解决方法。

我很难理解 Swift 的泛型系统。我似乎经常遇到这样看似简单的情况。我只是想把一个符合协议的东西放到另一个东西中:(。我想知道我的想法哪里出错了,这样我就可以修复它,再也不用看到这些错误了。

this related question,但请注意答案只提供解决方法,没有解释或解决方案。

【问题讨论】:

  • 查看问答Protocol doesn't conform to itself?(应该回答您的前 3 个(甚至可能是最后一个)问题)和Unable to use protocol as associatedtype in another protocol in Swift,应该回答您的最后一个问题。
  • 整个问题是协议并不总是符合它们自己——因此你不能使用抽象类型(例如ControllerSubscriber)作为符合AnyObject的具体类型。
  • 迟到的回复:感谢您提供的链接,这是一个很好的起点!从我目前所读的所有内容来看,似乎是“这是因为它是,但没有人知道为什么。”老实说,像这样的东西真的降低了我对 Swift 的热情。我没有看到自己为我想使用此订阅者模式的所有情况编写类型擦除的包装器。反正。在第二个链接中,您提出了几种替代解决方案。除了类型擦除的包装器之外,您在这种情况下是否看到任何替代方案?再次感谢您的回复。
  • 取决于您的subscribers 属性是否需要是符合ControllerSubscriber 的异构元素类型的NSHashTable。如果是这种情况,那么据我所知,类型擦除是您唯一的选择。如果您的subscribers 属性只需要是符合ControllerSubscriber 的同质元素的NSHashTable,那么您可以通过在Controller 中引入通用占位符来解决问题,以便在哈希表。尽管 AFAIK,但这些是目前解决您的问题的唯一两个实用解决方案。
  • 非常感谢您的帮助!

标签: ios swift macos generics


【解决方案1】:

把这个问题归咎于 Swift 可能是不公平的。关于类型的推理似乎是我们首先必须习惯的一些元艺术(除非您在过去 30 年一直担任 C++ 标准委员会成员 :-)。

原来您的问题与您选择NSHashTable 作为保存subscribers 的数据结构有关。以下内容将通过最小的更改进行编译:

protocol Subscribable: class {
    associatedtype Subscriber
    var subscribers: [Subscriber?] { get }
}

protocol ControllerSubscriber: class {
    func controllerDidSomething()
}

class Controller: Subscribable {
    typealias Subscriber = ControllerSubscriber
    var subscribers = [Subscriber?]()
}

但是,它缺少weak 语义,还没有真正有用。 subscribers 的列表显示为属性,必须由客户直接操作。此外,Subscribable 的每个实现都必须实现自己的通知机制,并且这种方法几乎没有集中任何逻辑。从技术上讲,您可以像这样使用它:

class Controller: Subscribable {
    typealias Subscriber = ControllerSubscriber
    var subscribers = [Subscriber?]()

    func notify() {
        for case let subscriber? in subscribers {
            subscriber.controllerDidSomething()
        }
    }
}

var controller = Controller()

class IWillSubscribe : ControllerSubscriber {
    func controllerDidSomething() {
        print("I got something")
    }
}

controller.subscribers.append(IWillSubscribe())
controller.notify()

但这既不实用也不可读。在 Java 7 之前,这将是一个可接受的解决方案(因为它是唯一的解决方案),但即使在 Java 8 中(在 Swift 中更是如此),我们希望将通知逻辑作为默认封装到 Subscribable 协议中实施,但这将是另一篇文章。

由于您选择将subscribers 实现为NSHashTable(这里可能有ARC 原因需要弱引用),因此似乎涉及到一些Objective-C 技巧。经过大量实验(最终找到this question 的第四个答案,我得到了以下工作:

protocol Subscribable: class {
    associatedtype Subscriber : AnyObject
    var subscribers: NSHashTable<Subscriber> { get }
}

@objc protocol ControllerSubscriber: class {
    func controllerDidSomething()
}

class Controller: Subscribable {
    typealias Subscriber = ControllerSubscriber
    var subscribers = NSHashTable<Subscriber>.weakObjects()

    func notify() {
        for subscriber in subscribers.allObjects {
            subscriber.controllerDidSomething()
        }
    }
}

var controller = Controller()

class IWillSubscribe : ControllerSubscriber {
    func controllerDidSomething() {
        print("I got something")
    }
}

let iDoSubscribe = IWillSubscribe()
controller.subscribers.add(iDoSubscribe)
controller.notify()

这几乎与您的原件相同(周围有一些证据)。看起来 Objective-C @protocols 与 Swift protocols 并不完全相同,但 Swift 可以实际上两者兼而有之。

这其中有很多微妙之处,只有 allObjects 在没有类型擦除的情况下有效,您可信赖的 objectEnumerator 只返回 Any?,这是一个愚蠢的动物,可以从中得到任何东西。还要注意

let iDoSubscribe = IWillSubscribe()

是有用的。一开始我试过了

controller.subscribers.add(IWillSubscribe())

它实际上在subscriberscount 中添加了一些东西,但是却放弃了任何迭代尝试(正如人们所期望的那样,weak 引用在其他任何地方都没有被引用)。

一个非常晚的答案已经太长了,只是为了证明这仍然是一个问题,即使是 Swift 3。也许一旦this Jira ticket 得到解决,这会变得更好。 p>

【讨论】:

    猜你喜欢
    • 2015-12-24
    • 1970-01-01
    • 1970-01-01
    • 2014-10-23
    • 2015-01-02
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多