Swift:ViewModel应该是一个结构还是类?

Pau*_*aul 14 struct closures mvvm swift

我试图在我的新项目中使用MVVM模式.第一次,我创建了我的所有视图模型到struct.但是当我使用闭包实现异步业务逻辑(如fetchDataFromNetwork)时,闭包会捕获旧的视图模型值,然后更新到那个.不是新的视图模型值.

这是操场上的测试代码.

import Foundation
import XCPlayground

struct ViewModel {
  var data: Int = 0

  mutating func fetchData(completion:()->()) {
    XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) {
      result in
      self.data = 10
      print("viewModel.data in fetchResponse : \(self.data)")
      completion()
      XCPlaygroundPage.currentPage.finishExecution()
      }.resume()
  }
}

class ViewController {
  var viewModel: ViewModel = ViewModel() {
    didSet {
      print("viewModel.data in didSet : \(viewModel.data)")
    }
  }

  func changeViewModelStruct() {
    print("viewModel.data before fetch : \(viewModel.data)")

    viewModel.fetchData {
      print("viewModel.data after fetch : \(self.viewModel.data)")
    }
  }
}

var c = ViewController()
c.changeViewModelStruct()
Run Code Online (Sandbox Code Playgroud)

控制台打印

viewModel.data before fetch : 0
viewModel.data in didSet : 0
viewModel.data in fetchResponse : 10
viewModel.data after fetch : 0
Run Code Online (Sandbox Code Playgroud)

问题是ViewController中的View Model没有新的Value 10.

如果我将ViewModel更改为类,则未调用didSet,但ViewController中的View Model具有新值10.

Cod*_*ode 7

你应该使用一个类.

如果使用具有变异函数的结构,则该函数不应在闭包内执行变异; 你应该没有做到以下几点:

struct ViewModel {
  var data: Int = 0

  mutating func myFunc() {
      funcWithClosure() {
          self.data = 1
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我将ViewModel更改为class,则不调用didSet

这里没有错 - 这是预期的行为.


如果您愿意使用struct,您可以这样做

  func fetchData(completion: ViewModel ->()) {
    XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) {
      result in
      var newViewModel = self
      newViewModel.data = 10
      print("viewModel.data in fetchResponse : \(self.data)")
      completion(newViewModel)
      XCPlaygroundPage.currentPage.finishExecution()
      }.resume()
  }


  viewModel.fetchData { newViewModel in
     self.viewModal = newViewModel
      print("viewModel.data after fetch : \(self.viewModel.data)")
    }
Run Code Online (Sandbox Code Playgroud)

另请注意,提供的闭包dataTaskWithURL不会在主线程上运行.你可能想要打电话dispatch_async(dispatch_get_main_queue()) {...}.

  • 尽管它的设计很糟糕,但通过使用类,编写单元测试要容易得多。(模拟结构体的方法确实是一项令人沮丧的任务) (2认同)