Для отношений «один к нескольким»: NotificationCenter или многоадресный делегат? - PullRequest
1 голос
/ 19 апреля 2019

Если делегаты были разработаны для отношений один-к-одному между объектами, а NSNotifications были разработаны для отношений один-к-многим, то есть ли лучший метод для отношений один-к-нескольким?

Я видел множество пользовательских многоадресных делегатов в iOS, где объект может быть приведен к нескольким подписчикам (например, Swift Language Multicast Delegate ), но реализации часто бывают очень сложными и кажутся излишними. Одной из таких проблем является безопасное хранение массива слабых ссылок (делегатов) ( Как мне объявить массив слабых ссылок в Swift? ).

Я видел много рекомендаций (таких как несколько слушателей для делегата iOS ), которые предполагают, что это то, для чего был создан NotificationCenter. Но сама идея вещания в эфир для отношений один-на-один сама по себе кажется излишней.

Есть ли лучшая практика для фреймворков Apple и языка Swift? Я никогда не видел, чтобы они писали об этом. Является ли NotificationCenter подходящим способом использования отношения один-к-нескольким, когда в противном случае потребовался бы многоадресный делегат?

1 Ответ

2 голосов
/ 19 апреля 2019

Я бы не стал использовать NotificationCenter, поскольку тип сообщения и данные между отправителем и получателем (наблюдателем) теряются.Использование Notification Center заставит ваш код полагаться на объект Notification, где вам нужно использовать словарь уведомлений userInfo для добавления данных, что затруднит понимание того, что именно содержит уведомление (нужно будет увидеть, насколько точноданные заполняются при отправке уведомления).

Делегат является лучшим решением, и если в слабом списке делегатов более 1 делегата, то это нормально.Я использовал такую ​​композицию во многих местах, где мне нужно зарегистрировать более одного слушателя на определенное событие и работает просто отлично.

Вы можете один раз создать коллекцию делегатов и очень легко использовать ее в коде.Вот мое решение:

class WeakContainer {

    private weak var value: AnyObject?

    public init(value: AnyObject) {
        self.value = value
    }

    func get() -> AnyObject? {
        return self.value
    }
}

class DelegatesCollection<T>: Sequence {

    private lazy var weakDelegates = [WeakContainer]()

    var delegates: [T] {
        return self.weakDelegates.map() { $0.get() as! T }
    }

    var hasDelegates: Bool {
        return !self.weakDelegates.isEmpty
    }

    init() { }

    func add(delegate: T) {
        var exists = false
        for currentDelegate in self.weakDelegates {
            if(currentDelegate.get() === (delegate as AnyObject)) {
                exists = true
                break
            }
        }

        if(!exists) {
            self.weakDelegates.append(WeakContainer(value: delegate as AnyObject))
        }
    }

    func remove(delegate: T) {
        var i = 0
        for currentDelegate in self.weakDelegates {
            if(currentDelegate.get() == nil || currentDelegate.get() === (delegate as AnyObject)) {
                self.weakDelegates.remove(at: i)
                break
            }

            i += 1
        }
    }

    func makeIterator() -> IndexingIterator<[T]> {
         return self.delegates.makeIterator()
    }
}

Я могу предположить, что платформы Apple используют только один делегат, потому что это бизнес-логика, какие действия выполнять при вызове делегата.С точки зрения Apple, достаточно делегировать, что произошло какое-то событие, и оставить приложение, чтобы решить, что делать дальше, поэтому нет смысла поддерживать несколько делегатов на уровне платформы.

...