为什么旧视图模型会响应通知?

时间:2018-01-12 16:27:11

标签: swift mvvm notifications

我正在使用MVVM创建一个举重计算器应用程序(Swift 4),并且已经尝试了2天来弄清楚为什么应该已经死亡的视图模型仍在响应UserDefaults.defaultsDidChange事件通知。

enter image description here

  1. 我启动了该应用:
    • 在发布时,在AppDelegate中,我创建了一个新的lift事件对象,并使用它为`CalculatorViewController':
    • 初始化一个新的CalculatorLiftEventViewModelFromLiftEvent
  2. 我计算升力并保存
  3. 点击+按钮创建新电梯:
    • 这会导致创建一个新的空提升事件对象
    • 此新的提升事件对象用于初始化新的CalculatorLiftEventViewModelFromLiftEvent对象
    • 然后将此新CalculatorLiftEventViewModelFromLiftEvent分配给CalculatorViewController的viewModel属性,替换应用启动时创建的
    • 计算器屏幕上的值已清零,准备好输入新的升降机事件
  4. 点击“设置”按钮转到“设置”,我在其中更改与当前电梯事件关联的公式。
  5. 新公式将保存为默认值并触发UserDefaults.defaultsDidChange通知
  6. 这是我无法想象的部分:原始视图模型仍处于活动状态,它仍在监听UserDefault通知。当我关闭“设置”屏幕并返回“计算器”视图时,现在已清除的先前提升事件的值重新出现。
  7. 点击计算器屏幕上的+(新)按钮时会发生以下情况:

    @objc fileprivate func onNewButtonTapped(_ sender: UIBarButtonItem) {
        let newLiftEvent = dataManager.createNewLiftEvent()
        viewModel = CalculatorLiftEventViewModelFromLiftEvent(withLiftEvent: newLiftEvent, dataManager: dataManager)
    
        setupView()
    }
    

    以下是CalculatorLiftEventViewModelFromLiftEvent的初始化方式:

    init(withLiftEvent liftEvent: LiftEventRepresentable, dataManager: CoreDataHelper) {
        self.modelLiftEvent = liftEvent
        self.liftName = Dynamic("\(modelLiftEvent.lift.liftName)")
        self.weightLiftedTextField = Dynamic(modelLiftEvent.liftWeight.value)
        self.repetitionsTextField = Dynamic("\(modelLiftEvent.repetitions)")
        self.oneRepMaxTextField = Dynamic(modelLiftEvent.oneRepMax.value)
        self.unitsTextField = Dynamic("\(UserDefaults.weightUnit())")
        self.weightPercentages = Dynamic( [ : ] )
        self.dataManager = dataManager
    
        super.init()
    
        subscribeToNotifications()
    }
    

    更新:以下是deinit中的addObserverCalculatorLiftEventViewModelFromLiftEvent。请注意,我没有使用基于块的观察。

    deinit {
        print("I got to the deinit method")
        unsubscribeFromNotifications()
    }
    
    func subscribeToNotifications() {
            NotificationCenter.default.addObserver(self,
                                                        selector: #selector(liftNameDidChangeNotification(_:)),
                                                        name: NSNotification.Name(rawValue: LiftEventNotifications.LiftNameDidChangeNotification),
                                                        object: nil)
    
            NotificationCenter.default.addObserver(self,
                                                        selector: #selector(weightUnitDidChangeNotification(_:)),
                                                        name: NSNotification.Name(rawValue: LiftEventNotifications.WeightUnitDidChangeNotification),
                                                        object: nil)
    
            NotificationCenter.default.addObserver(self,
                                                        selector: #selector(roundingOptionDidChangeNotification(_:)),
                                                        name: NSNotification.Name(rawValue: UserDefaultsNotifications.roundingOptionDidChangeNotification),
                                                        object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(self.defaultsDidChange), name: UserDefaults.didChangeNotification,
                                                        object: nil)
        }
    

    --- END UPDATE

    我在转到SettingsViewController时传递了modelLiftEvent:

     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if let identifier = segue.identifier {
                switch identifier {
                case a:...
                case b:...
                case "SettingsSegue":
                if let nav = segue.destination as? UINavigationController {
                    let destinationViewController = nav.topViewController as! SettingsViewController
                    destinationViewController.dismissalDelegate = self
    
                    let settingsViewModel = SettingsViewModelFromLiftEvent(withLiftEvent: self.viewModel.modelLiftEvent)
                    destinationViewController.settingsViewModel = settingsViewModel
                    destinationViewController.dataManager = dataManager
                    settingsViewModel.dataManager = dataManager
                }
    

    最后,在CalculatorLiftEventViewModelFromLiftEvent中,我在此处设置了一个断点,因为当视图模型听到UserDefaults.defaultsDidChange通知时会调用此点。此时,我还验证了这个CalculatorLiftEventViewModelFromLiftEvent是旧版本,而不是我点击+按钮时创建的新版本:

    @objc func defaultsDidChange(_ notification: Notification) {
        let oneRepMax = modelLiftEvent.calculateOneRepMax()
    
        guard oneRepMax.value != 0.0 else { return }
    
        let weightPercentages = getWeightPercentages(weight: oneRepMax.value)
        self.weightPercentages.value = weightPercentages
    
        weightLiftedTextField.value = modelLiftEvent.liftWeight.value
        repetitionsTextField.value = "\(modelLiftEvent.repetitions)"
        oneRepMaxTextField.value = modelLiftEvent.oneRepMax.value
    }
    

    我已经阅读了大量关于物品生命周期的文档,但却找不到任何有用的东西。我希望当创建新的CalculatorLiftEventViewModelFromLiftEvent并将其分配给`CalculatorViewController的viewModel属性时,它将替换对旧的$content = new \SendGrid\Content("text/html", "<html><body>some text here</body></html>"); $mail = new \SendGrid\Mail($from, $subject, $to, $content); 的引用,它将不再存在。显然,这不是发生的事情。

    有没有人知道为什么当我从没有值(除了0.0)的计算器视图(步骤3)进入设置然后再回来时,会显示先前的提升事件值?

1 个答案:

答案 0 :(得分:0)

我已经修复了清除计算器,更改默认公式以及返回计算器屏幕后显示的先前liftEvent的问题。

CalculatorViewController上,点击+按钮时,我不是创建新的viewModel并将其分配给viewModel属性,而是要求我的AppDelegate同时创建一个新的CalculatorViewControllerCalculatorLiftEventViewModelFromLiftEvent使用launchCalculatorViewController方法在应用启动时执行此操作。

CalculatorViewController中的原始代码:

@objc fileprivate func onNewButtonTapped(_ sender: UIBarButtonItem) {

    let newLiftEvent = dataManager.createNewLiftEvent()
    viewModel = CalculatorLiftEventViewModelFromLiftEvent(withLiftEvent: newLiftEvent, dataManager: dataManager)

    self.percentagesTableView.reloadData()

    setupView()
}

现在CalculatorViewController中的新代码:

@objc fileprivate func onNewButtonTapped(_ sender: UIBarButtonItem) {

    (UIApplication.shared.delegate as? AppDelegate)?.launchCalculatorViewController()

}

AppDelegate

func launchCalculatorViewController() {
    self.window = UIWindow(frame: UIScreen.main.bounds)
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    if let initialViewController: CalculatorViewController = mainStoryboard.instantiateInitialViewController() as? CalculatorViewController {

        self.window?.rootViewController = initialViewController

        let liftEvent = dataManager.createNewLiftEvent()
        let viewModel = CalculatorLiftEventViewModelFromLiftEvent(withLiftEvent: liftEvent, dataManager: dataManager)
        initialViewController.viewModel = viewModel
        initialViewController.dataManager = dataManager
        self.window?.makeKeyAndVisible()

    }
}

不幸的是,我确定CalculatorLiftEventViewModelFromLiftEvent对象永远不会被释放,这告诉我,我有一个强大的参考周期,不会放手:

enter image description here

这将是另一个问题。