我正在使用 MVVM 创建一个举重计算器应用程序 (Swift 4),并且已经尝试了 2 天来找出为什么本应死亡的 View 模型仍在响应 UserDefaults.defaultsDidChange
事件通知。
- 我启动应用程序:
- 启动时,在 AppDelegate 中,我创建一个新的 lift 事件对象,并使用它为 `CalculatorViewController' 初始化一个新的
CalculatorLiftEventViewModelFromLiftEvent
:
- 启动时,在 AppDelegate 中,我创建一个新的 lift 事件对象,并使用它为 `CalculatorViewController' 初始化一个新的
- 我计算升力并保存
- 我点击 + 按钮创建新的电梯:
- 这会导致创建一个新的空电梯事件对象
- 这个新的 lift 事件对象用于初始化新的
CalculatorLiftEventViewModelFromLiftEvent
对象 - 然后,这个新的
CalculatorLiftEventViewModelFromLiftEvent
会分配给CalculatorViewController
的 viewModel 属性,替换应用启动时创建的属性 - 计算器屏幕上的值已清零,准备输入新的电梯事件
- 我点击“设置”按钮转至“设置”,在其中更改与当前提升事件关联的公式。
- 新公式将保存为默认值,并且会触发
UserDefaults.defaultsDidChange
通知 - 这是我无法弄清楚的部分:原始 View 模型仍然存在,并且仍在监听 UserDefault 通知。当我关闭“设置”屏幕并返回“计算器” View 时,先前已清除的电梯事件的值现在会重新出现。
以下是点击计算器屏幕上的 +(新)按钮时会发生的情况:
@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()
}
更新:以下是 CalculatorLiftEventViewModelFromLiftEvent
中的 deinit
和 addObserver
。请注意,我没有使用基于 block 的观察。
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
中,我在此处放置了一个断点,因为当 View 模型听到 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 除外)的计算器 View (步骤 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问题。
关于swift - 为什么旧 View 模型会响应通知?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48230283/