Swift - metode pemicu di subkelas ketika properti superkelas berubah

Saya memiliki tiga superclass dengan 2 subclass sebagai berikut:

class BaseLookupViewController {
}

class RetrieveCompletedordersViewController: BaseLookupViewController {
}

class LoginViewController: BaseLookupViewController {
}

Di BaseLookupViewController saya memiliki properti 'deviceOnline':

internal var deviceConnected = true

Ini diatur menggunakan pengamat dari kelas yang berbeda:

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
    }
}

Ketika bool disetel, saya perlu memicu metode tertentu di setiap subkelas. Misalnya, di RetrieveCompletedordersViewController saya perlu memicu yang berikut ini:

    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
    }

Dan metode serupa tetapi jelas pada properti berbeda di subkelas lain. Saya benar-benar ingin menghindari menambahkan pengamat properti ke setiap subkelas karena tampaknya tidak diperlukan. Cara paling bersih tampaknya adalah dengan mengatur properti subkelas dalam metode willSet 'deviceConnected' tetapi karena kelas-kelas ini bukan BaseLookupViewController yang lajang. Apa cara terbersih untuk mencapai hal ini tanpa kode berulang?


person DevB1    schedule 20.07.2020    source sumber


Jawaban (1)


Nah, karena metode yang dipanggil bergantung pada tipe, maka harus ada objek yang bertanggung jawab untuk melakukan panggilan tersebut, cara paling mudah dan logis untuk melakukannya yang bisa saya lihat adalah membuat metode tersebut mengatur dirinya sendiri, yang berarti bahwa setiap subkelas harus menangani pemanggilan metode apa pun. perlu dipanggil ketika properti deviceConnected diubah.

Anda dapat mengganti properti tersebut di setiap subkelas dan menambahkan kode pemanggilan metode di didSet, Anda tidak akan kehilangan kemampuan superkelas apa pun karena properti tersebut masih merupakan properti tersimpan yang ditentukan oleh superkelasnya, yang berarti, jika Anda melakukan upcast ke superkelas Anda masih memiliki akses ke properti itu karena penggantian tidak membatalkan deklarasi asli.

Sesuatu seperti ini:

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
            }
        }
    }

}

Agar didSet subkelas tertentu dapat dipanggil, Anda harus menetapkan nilai properti pada instance subkelas itu sendiri, jika Anda melakukan upcast dan menetapkan nilai pada superkelas, maka nilai tersebut tidak akan dieksekusi, yang secara efektif adalah apa yang Anda lakukan dengan memanggil # selector langsung di superclass. Untuk mengatasi masalah ini, tambahkan pengamat notifikasi dengan penutupan, ini akan menangkap subkelas sebagai self dan mengeksekusi didSet dengan benar:

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
           })
    )
}

Ingatlah untuk selalu menghapus pengamat dari Pusat Pemberitahuan ketika tidak lagi menggunakan pemberitahuan, seperti ketika objek dideinisialisasi, saya ingin melakukan ini dengan array untuk menangani pembersihan:

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
Terima kasih atas balasannya, saya sangat menyukai metode ini. Namun, override var didSet saya tidak dipanggil dengan cukup aneh ketika properti disetel di superclass :/ - person DevB1; 21.07.2020
comment
Maaf, saya salah, dengan menggunakan pemilih Anda tidak menangkap diri sendiri, periksa jawaban yang diperbarui dan coba :) - person Gilberto Valadez; 21.07.2020
comment
hmmm aneh, masih belum berhasil. Saya akan bermain-main dan melihat apakah saya bisa mengetahuinya. Terima kasih banyak atas masukannya - person DevB1; 21.07.2020