观察者通信模式

Observer Communication Pattern

我有一个非常简单的实现 (3 类) 来获取基础知识。但是有BUG。

事实:它通知 ViewController,但它没有通知 SecondVC 的屏幕。想知道为什么或在哪里!

Git: https://github.com/marlhex/ObserverPattern

相关类:

struct MyNotifications {
    static let broadcast = "BROADCAST"
}
import UIKit

let notificationCtr = NotificationCenter.default

class ViewController: UIViewController {

    @IBOutlet weak var myLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        notificationCtr.addObserver(self, selector: #selector(notifyObservers), name: NSNotification.Name(rawValue: MyNotifications.broadcast), object: nil)
    }
    
    @objc func notifyObservers() {
        myLabel.text = "I got Notified"
    }
    
    @IBAction func doBroadcast(_ sender: Any) {
        notificationCtr.post(name: NSNotification.Name(rawValue: MyNotifications.broadcast), object: self)
    }

}
import UIKit

class SecondVC: UIViewController {
    
    @IBOutlet weak var mySecondLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        notificationCtr.addObserver(self, selector: #selector(notifyObserverstoo), name: NSNotification.Name(rawValue: MyNotifications.broadcast), object: nil)
    }
   
    @objc func notifyObserverstoo() {
        mySecondLabel.text = "I got Notified too" //Bug only notifies one view controller
    }
}

您正在使用 UITabBarController 来托管视图。系统最初只加载它需要显示的视图控制器(在本例中为 ViewController)。然后,一旦您单击 SecondVC 的选项卡,它就会加载该视图控制器。

您可以通过在 SecondVCviewDidLoad 中放置一个 print 语句来验证这一点。

您还可以验证如果在返回 ViewController 并按 Notify 之前导航到 SecondVC,在这种情况下两个视图控制器都会收到通知。

所以,这不是错误 -- 它只是加载视图时的实现细节。

如果您想找到一种方法来确保 SecondVC 在加载时可以访问该信息,您有两个选择:

  1. 依靠不同的系统传播状态
  2. 将您的通知侦听器放在 required init?(coder: NSCoder) 而不是 viewDidLoad 中(这会在设置过程中调用)。 这里有一个警告UILabel 尚未加载,因此您必须存储该状态以便稍后加载。在 viewDidLoad 之前尝试访问 mySecondLabel 将导致崩溃。

更新 更新了在您要使用 init 方法的事件中存储通知的代码:

class SecondVC: UIViewController {
    
    // MARK: - Properties
    @IBOutlet weak var mySecondLabel: UILabel?
    
    var haveBeenNotified = false //to store whether the notification has been seen
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        notificationCtr.addObserver(self, selector: #selector(notifyObserverstoo), name: NSNotification.Name(rawValue: MyNotifications.broadcast), object: nil)
    }
    
    // MARK: - Life Cycle Methods
    override func viewDidLoad() {
        super.viewDidLoad()
        
        print("Loaded second view controller")
        
        if haveBeenNotified {
            mySecondLabel?.text = "Was notified before viewDidLoad"
        }
    }
    
    // MARK: - Observer Selector Functions
    @objc func notifyObserverstoo() {
        haveBeenNotified = true
        mySecondLabel?.text = "I got Notified too"
    }

}