Ответ 1
Вероятно, несправедливо обвинять эту проблему в Swift. Рассуждение о типах кажется некоторым мета-искусством, с которым нам сначала придется привыкнуть (если вы не сидите в комитете по стандартам С++ за последние 30 лет, то есть:-).
Оказывается, ваша проблема связана с вашим выбором 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. После много экспериментов (и, наконец, найдя четвертый ответ на этот вопрос, я получил следующее:
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
s, но Swift действительно может это сделать.
В этом довольно много тонкости, но только allObjects
работает без стирания типа, ваш верный objectEnumerator
возвращает Any?
, и это глупое животное, чтобы получить что-либо. Также обратите внимание, что
let iDoSubscribe = IWillSubscribe()
является инструментальным. Сначала я попробовал
controller.subscribers.add(IWillSubscribe())
который фактически добавил что-то в count
из subscribers
, но ушел с любой попыткой итерации (как следует ожидать от ссылки weak
, которая не упоминается нигде).
Очень поздний ответ, который уже слишком длинный, просто чтобы доказать, что это все еще проблема, даже с Swift 3. Возможно, это улучшится после этот билет Jira разрешен.