我试图实现一个简单的多委托情况:
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
}
错误:使用&#39; ControllerSubscriber&#39;作为符合协议的具体类型的任何对象&#39;不受支持。
我的问题是:
当然,我该如何解决这个问题?从实际的解决方案来看,不是解决方法。
我很难理解Swift的泛型系统。我似乎经常遇到这样看似简单的情况。我只是想把一个符合协议的东西放到另一个东西:(。我想知道我的思路出错了所以我可以修复它,再也不用看这些错误了。
有this related question,但请注意答案仅提供解决方法,无解释或解决方案。
答案 0 :(得分:3)
将这个问题归咎于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 @protocol
不像完全那样与Swift protocol
相同,但是Swift 可以实际执行任何操作。
虽然这里有很多细微之处,只有allObjects
没有类型擦除,你的可靠objectEnumerator
只返回Any?
,这是一个愚蠢的动物,可以从中获取任何东西。另请注意
let iDoSubscribe = IWillSubscribe()
是有帮助的。起初我试过
controller.subscribers.add(IWillSubscribe())
实际上向count
的{{1}}添加了一些内容,但是在任何迭代尝试都没有进行(正如人们应该期望的subscribers
引用在其他任何地方都没有引用)
一个非常晚的答案已经太长了,只是为了证明这仍然是仍然一个问题,即使使用Swift 3.也许这会在this Jira ticket解决后变得更好。