Swift - วิธีการทริกเกอร์ในคลาสย่อยเมื่อคุณสมบัติซูเปอร์คลาสเปลี่ยนแปลง

ฉันมีซูเปอร์คลาสสามคลาสพร้อมคลาสย่อย 2 คลาสดังนี้:

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

เมื่อตั้งค่าบูลแล้ว ฉันจำเป็นต้องทริกเกอร์เมธอดเฉพาะในแต่ละคลาสย่อย ตัวอย่างเช่น ใน RestoreeCompletedordersViewController ฉันต้องการทริกเกอร์สิ่งต่อไปนี้:

    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 ถูกเรียก คุณต้องตั้งค่าคุณสมบัติบนอินสแตนซ์ของคลาสย่อยเอง หากคุณอัปแคสต์และตั้งค่าบนซูเปอร์คลาส มันจะไม่ถูกดำเนินการ ซึ่งเป็นสิ่งที่คุณทำอย่างมีประสิทธิภาพโดยการเรียก # ตัวเลือกโดยตรงในซูเปอร์คลาส ในการแก้ปัญหานี้ ให้เพิ่มผู้สังเกตการณ์การแจ้งเตือนด้วยการปิดแทน สิ่งนี้จะจับคลาสย่อยเป็นตัวเองและดำเนินการ 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