简体   繁体   English

MVVM-使用闭包将ViewModel与数据源绑定:是否需要捕获列表?

[英]MVVM - using closures to bind a ViewModel with a DataSource: capture list needed?

I have a viewController keeping a (strong) reference to its viewModel 我有一个viewController保持(strong)对其viewModel引用

lazy private var viewModel: ListViewModel = {
   return ListViewModel()
}()

override func viewDidLoad() {
   super.viewDidLoad()

   initViewModel()
}

private func initViewModel() {
   viewModel.onModelChange = { [weak self] () in
      DispatchQueue.main.async {
         self?.tableView.reloadData()
      }
   }

   viewModel.fetchData()
}

Apple´s Swift documentation days that: Apple的Swift文档日:

A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance. 如果将闭包分配给类实例的属性,并且闭包的主体捕获该实例,则也会发生强引用循环。

This viewController holds a viewController which holds a closure that captures the viewController , so I understand a strong reference cycle could occur. viewController持有一个viewController ,其中包含一个捕获了viewController的闭包,因此我了解可能会发生强大的引用周期。

These are the viewModel 's properties: 这些是viewModel的属性:

var onModelChange: (() -> Void)?

private var model: [MyModel] = [MyModel]() {
    didSet {
        self.onModelChange?()
    }
}

On the other hand, this viewController is the root one, so won´t be deallocated during the app's life. 另一方面,此viewControllerroot ,因此在应用程序生命周期内不会被释放。 And I don´t explicitly nilify its viewModel property. 而且我没有明确取消viewModel属性。 So: 所以:

  • Question 1 : is there actually a risk of a strong reference cycle happening? 问题1 :实际上是否存在发生强大参考周期的风险? Should I provide a capture list to the closure anyway? 无论如何,我是否应该为封包提供捕获列表?
  • Question 2 : I set the capture list to be [weak self] but, since it seems the viewController and its viewModel would be deallocated same time, I guess I should have defined [unowned self] instead? 问题2 :我将捕获列表设置为[weak self]但是,由于似乎viewController及其viewModel将同时释放,因此我想应该应该定义[unowned self]吗? Does it hurt to define [weak self] in case of doubt? 如果有疑问,定义[weak self]是否有害?

Let's think that now the viewController is one that is pushed on top of the root one, so it could be pop during the life of the app. 让我们考虑一下,现在viewController是一个被推到root之上的viewController ,因此在应用程序的生命周期中它可能会弹出。 The viewController 's fetchData() method is like this: viewControllerfetchData()方法如下所示:

func fetchData() {
    dataSource.getData(completion: { (result, error) -> Void in
        if error != nil {
            self.model = [MyModel]()
        } else {
            if let result = result {
                self.model = result
            } else {
                self.model = [MyModel]()
            }
        }
    })
}

where dataSource is a strong property and could get the data from either a local file (sync task) or to call a service (asyn task). 其中dataSource是强属性,可以从本地文件(同步任务)或调用服务(异步任务)获取数据。

The model is also a property that is observed and calls the closure provided from the viewController model也是一个观察到的属性,它调用viewController提供的闭包

So: 所以:

  • Question 3 : in case the dataSource is getting the data from a local file. 问题3 :如果dataSource从本地文件获取数据。 What happens then if the viewController is pop in the meanwhile? 如果同时弹出viewController会发生什么呢? Would the viewModel still be alive until the dataSource finishes? dataSource完成之前, viewModel仍将处于活动状态? In such case, is it correct to provide the [weak self] capture list in the closure? 在这种情况下,在闭包中提供[weak self]捕获列表是否正确?
  • Question 4 : in case the dataSource is performing an async network call, and the viewController is pop in the meanwhile, would be the same scenario than before? 问题4 :如果dataSource正在执行异步网络调用,并且viewController同时弹出,那么情况是否会与以前相同? I mean, would the viewModel be still alive until the dataSource finishes? 我的意思是,在dataSource完成之前, viewModel是否仍会存在?

EDIT: Scenario/Question 5 编辑: 场景/问题5

Now my viewModel asks also an object UserSettingsManager to get some other data: 现在,我的viewModel还要求对象UserSettingsManager获取其他数据:

func getUserSettings() {
    UserSettingsManager.getInfo { (result, error) in
        if error == nil {
            self.userData = result
            }
        }
    }
}

But this UserSettingsManager is not a property of the viewModel as dataSource is, it is a class that only provides static func . 但是,这UserSettingsManager不是的属性viewModel作为dataSource ,它是一类仅提供static func Those static func are passed closures as @escaping , so I guess a strong reference cycle could also happen in this scenario, right? 那些static func通过@escaping传递给闭包,所以我猜在这种情况下也可能发生强大的引用周期,对吗? Or would this scenario be different from the previous ones? 还是这种情况与以前的情况不同? Which capture list would I need here (if any)? 我在这里需要哪个捕获列表(如果有)?

Question 1: is there actually a risk of a strong reference cycle happening? 问题1:实际上有发生强大参考周期的风险吗? Should I provide a capture list to the closure anyway? 无论如何,我是否应该为封包提供捕获列表?

The strong reference cycle will happen. 强大的参考周期将会发生。 The question should be whether or not the strong reference cycle matters. 问题应该是强参考周期是否重要。 In this case probably not, but it would probably be a good idea to keep the weak reference anyway, just in case you move the controller in the future. 在这种情况下可能不会,但是无论如何保持弱引用还是一个好主意,以防万一您将来移动控制器。 It's a small price to pay for future safety. 为将来的安全付出很小的代价。

Question 2: I set the capture list to be [weak self] but, since it seems the viewController and its viewModel would be deallocated same time, I guess I should have defined [unowned self] instead? 问题2:我将捕获列表设置为[弱自我],但是,由于似乎viewController及其viewModel将同时释放,因此我想应该应该定义[unown self]吗? Does it hurt to define [weak self] in case of doubt? 如果有疑问,定义[弱自我]是否有害?

[unowned self] implies that self will definitely exist when the closure is called. [unowned self]表示在调用闭包时肯定会存在self Unless you are absolutely sure that's the case, you should stick with [weak self] . 除非您绝对确定是这种情况,否则您应该坚持[weak self] My preference is to always use weak as it's not to much extra effort to safely unwrap, and you avoid a crash if you somehow missed a case where self could be deallocated. 我的偏好是始终使用weak ,因为不必花费太多精力就可以安全地解开包装,并且如果您以某种方式错过了可以将self释放的情况,则可以避免崩溃。 If you use self many times in a closure it might be nice to unwrap it in a guard statement or move the code to a separate function. 如果您在闭包中多次使用self ,那么最好将其在guard语句中解包,或者将代码移至单独的函数中。

Question 3: in case the dataSource is getting the data from a local file. 问题3:如果dataSource从本地文件获取数据。 What happens then if the viewController is pop in the meanwhile? 如果同时弹出viewController会发生什么呢? Would the viewModel still be alive until the dataSource finishes? 在数据源完成之前,viewModel是否仍将处于活动状态? In such case, is it correct to provide the [weak self] capture list in the closure? 在这种情况下,在闭包中提供[弱自我]捕获列表是否正确?

In your example you have a strong reference to self in the closure, so you have a strong reference cycle which means the ViewController will never be deallocated. 在您的示例中,您在闭包中对self有很强的引用,因此您有一个强大的引用周期,这意味着ViewController将永远不会被释放。

Either way, if you grab the data synchronously, a tap to dismiss the view controller won't get to do it's work until after the fetch from the file and the code in the block is complete. 无论哪种方式,如果您同步抓取数据,那么在从文件中获取数据和块中的代码完成之后,轻按以关闭视图控制器就无法完成工作。 Those would have to complete before the main thread becomes unblocked and the code to dismiss the controller can execute. 这些操作必须在主线程变得不受阻塞并且可以执行解除控制器的代码之前完成。

Question 4: in case the dataSource is performing an async network call, and the viewController is pop in the meanwhile, would be the same scenario than before? 问题4:如果dataSource正在执行异步网络调用,并且viewController同时弹出,那么情况是否会与以前相同? I mean, would the viewModel be still alive until the dataSource finishes? 我的意思是,在数据源完成之前,viewModel是否仍会存在?

If you made it a weak reference and dataSource was retained outside the controller so it outlives the controller, it is possible that self could be nil when the closure is called. 如果您将其设为弱引用,并且dataSource保留在控制器外部,从而使它的寿命超出控制器,则在调用闭包时, self可能为nil。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM