我正在使用MVVM创建举重计算器应用程序(Swift 4),并且已经尝试了2天,以弄清楚为什么应该死亡的视图模型仍然响应UserDefaults.defaultsDidChange
事件通知。
我启动该应用程序:
在启动时,在AppDelegate中,我创建了一个新的提升事件对象,并使用它来为CalculatorViewController初始化一个新的CalculatorLiftEventViewModelFromLiftEvent
:
我计算升力并保存
我点击+按钮创建一个新的提升:
这将导致创建一个新的空提升事件对象
此新的提升事件对象用于初始化新的CalculatorLiftEventViewModelFromLiftEvent
对象
然后,将此新的CalculatorLiftEventViewModelFromLiftEvent
分配给CalculatorViewController
的viewModel属性,替换应用程序启动时创建的属性
计算器屏幕上的值被清零,准备输入新的提升事件
我点击“设置”按钮转到“设置”,在其中更改与当前提升事件关联的公式。
新公式将保存为默认公式,并触发UserDefaults.defaultsDidChange
通知
这是我无法确定的部分:原始视图模型仍然有效,并且仍在监听UserDefault通知。当我关闭“设置”屏幕并返回到“计算器”视图时,现在会再次显示以前清除的提升事件中的值。
轻按“计算器”屏幕上的+(新)按钮会发生以下情况:
@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
和addObserver
中的CalculatorLiftEventViewModelFromLiftEvent
。注意,我没有使用基于块的观察。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)
}
-结束更新
我在选择
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属性时,它将替换对旧的引用,并且该引用将不复存在。显然,这不是正在发生的事情。有谁知道为什么当我从没有值(0.0除外)的“计算器”视图(第3步)转到“设置”,然后返回时,会显示以前的举升事件值?
最佳答案
我已经解决了在清除计算器,更改默认公式并返回到计算器屏幕之后显示先前的liftEvent
的问题。
在CalculatorViewController
上,当点击+按钮时,不是要创建新的viewModel并将其分配给viewModel属性,而是要我的AppDelegate通过使用CalculatorViewController
创建新的CalculatorLiftEventViewModelFromLiftEvent
和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
对象永远不会被释放,这告诉我我有一个强大的参考周期,它不会放过:那将是另一个SO问题。