Swift - метод запуска в подклассе при изменении свойства суперкласса

У меня есть три суперкласса с двумя подклассами следующим образом:

class BaseLookupViewController {
}

class RetrieveCompletedordersViewController: BaseLookupViewController {
}

class LoginViewController: BaseLookupViewController {
}

В BaseLookupViewController у меня есть свойство "deviceOnline":

internal var deviceConnected = true

Это устанавливается с помощью наблюдателя из другого класса:

override public func viewDidLoad() {
    super.viewDidLoad()
    stackView.customPadding(Constants.stackViewPadding, after: submitButton)
    addConectivityObserver()
}


// MARK: - Configuration

private func addConectivityObserver() {
    NotificationCenter.default.addObserver(self, selector: #selector(connectivityChanged(notification:)), name: .connectivityStatusChanged, object: nil)
}

@objc private func connectivityChanged(notification: Notification) {
    if notification.object as? ConnectionService.ConnectionStatus == .connected {
        deviceConnected = true
    } else {
        deviceConnected = false
    }
}

Когда установлено логическое значение, мне нужно вызвать определенные методы в каждом из подклассов. Например, в RetrieveCompletedordersViewController мне нужно запустить следующее:

    if deviceConnected {
        self.tabBarItem.image = #imageLiteral(resourceName: "retrieve_completed")
        self.errorLabel?.isHidden = true
    } else {
        self.tabBarItem.image = #imageLiteral(resourceName: "retrieve_offline")
        self.errorLabel?.isHidden = false
    }

И аналогичный метод, но, очевидно, для другого свойства в другом подклассе. Я действительно хочу избежать добавления наблюдателя свойств к каждому из подклассов, поскольку это кажется ненужным. Кажется, что самый чистый способ - каким-то образом установить свойства подклассов в методе willSet для 'deviceConnected', но поскольку эти классы не являются синглтонами BaseLookupViewController. Каков самый чистый способ добиться этого без повторяющегося кода?


person DevB1    schedule 20.07.2020    source источник


Ответы (1)


Что ж, поскольку вызываемые методы зависят от типа, должен быть объект, ответственный за выполнение указанных вызовов, самый простой и логичный способ сделать это, который я вижу, - заставить их управлять собой, что означает, что каждый подкласс должен обрабатывать вызовы любых методов. необходимо вызывать при изменении свойства deviceConnected.

Вы можете переопределить указанное свойство в каждом подклассе и добавить код вызова метода в didSet, вы не потеряете никаких возможностей суперкласса, потому что это все еще хранимое свойство, определенное его суперклассом, что означает, что если вы должны были выполнить преобразование в суперкласс у вас по-прежнему будет доступ к этому свойству, поскольку переопределение не делает недействительным исходное объявление.

Что-то вроде этого:

class RetrieveCompletedordersViewController: BaseLookupViewController {

    override var deviceConnected: Bool {
        didSet {
            if deviceConnected {
                tabBarItem.image = #imageLiteral(resourceName: "retrieve_completed")
                errorLabel?.isHidden = true
            } else {
                tabBarItem.image = #imageLiteral(resourceName: "retrieve_offline")
                errorLabel?.isHidden = false
            }
        }
    }

}

Чтобы вызвать специфичный для подкласса didSet, вы должны установить значение свойства в самом экземпляре подкласса, если вы выполните преобразование и установите значение в суперклассе, он не будет выполнен, что фактически вы делаете, вызывая # селектор непосредственно в суперклассе. Чтобы решить эту проблему, вместо этого добавьте наблюдатель уведомлений с закрытием, это зафиксирует подкласс как self и правильно выполнит didSet:

var notificationObservers: Array<NSObjectProtocol> = []

override public func viewDidLoad() {
    super.viewDidLoad()
    stackView.customPadding(Constants.stackViewPadding, after: submitButton)
    notificationObservers.append(
            NotificationCenter.default.addObserver(
                forName: .connectivityStatusChanged,
                object: nil,
                queue: .main,
                using: { [weak self] (notification) in
                           if notification.object as? ConnectionService.ConnectionStatus == .connected {
                               self?.deviceConnected = true
                           } else {
                               self?.deviceConnected = false
           })
    )
}

Не забывайте всегда удалять наблюдателя из Центра уведомлений, когда уведомление больше не используется, например, когда объект деинициализируется. Мне нравится делать это с помощью массива для обработки очистки:

deinit {
        // Perform cleanup of any Apple Notification Center notifications to which we may have registered
        notificationObservers.forEach { (observer) in
            NotificationCenter.default.removeObserver(observer)
        }
    }
person Gilberto Valadez    schedule 20.07.2020
comment
Спасибо за ответ на это, мне определенно нравится этот метод. Однако мое переопределение var didSet не вызывается достаточно странно, когда свойство устанавливается в суперклассе:/ - person DevB1; 21.07.2020
comment
Извините, я ошибся, используя селектор, вы не захватываете себя, проверьте обновленный ответ и попробуйте :) - person Gilberto Valadez; 21.07.2020
comment
хм, странно, до сих пор не попал. Я собираюсь поиграть и посмотреть, смогу ли я понять это. Большое спасибо за вклад - person DevB1; 21.07.2020