Swift:ViewModel应该是结构还是类?
我正在尝试在我的新项目中使用MVVM模式。第一次,我将所有视图模型都创建为struct。但当我使用闭包实现异步业务逻辑(如fetchDataFromNetwork)时,闭包捕获旧的视图模型值,然后更新为该值。不是新的视图模型值 这里是一个在操场上的测试代码Swift:ViewModel应该是结构还是类?,swift,mvvm,struct,closures,Swift,Mvvm,Struct,Closures,我正在尝试在我的新项目中使用MVVM模式。第一次,我将所有视图模型都创建为struct。但当我使用闭包实现异步业务逻辑(如fetchDataFromNetwork)时,闭包捕获旧的视图模型值,然后更新为该值。不是新的视图模型值 这里是一个在操场上的测试代码 import Foundation import XCPlayground struct ViewModel { var data: Int = 0 mutating func fetchData(completion:()-&g
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()
控制台打印
viewModel.data before fetch : 0
viewModel.data in didSet : 0
viewModel.data in fetchResponse : 10
viewModel.data after fetch : 0
问题是ViewController中的视图模型没有新值10
如果我将ViewModel更改为class,则未调用didSet,但ViewController中的View Model具有新值10。您应该使用一个类 如果使用带有变异函数的结构,则该函数不应在闭包内执行变异;您应该而不是执行以下操作:
struct ViewModel {
var data: Int = 0
mutating func myFunc() {
funcWithClosure() {
self.data = 1
}
}
}
如果我将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)")
}
还要注意,提供给
dataTaskWithURL
的闭包不在主线程上运行。您可能需要在其中调用dispatch\u async(dispatch\u get\u main\u queue()){…}
。您可以通过两个选项获取self.data
:或者在闭包中为fetchResponse
(使用viewModel
作为结构)或者您可以创建自己的set方法/闭包,并在init
方法中使用它(使用viewModel
作为class
)
控制台打印:
viewModel.data in didSet : 0
viewModel.data before fetch : 0
viewModel.data in fetchResponse : 10
viewModel.data after fetch : 10
他这样说:
首次初始化属性时不调用willSet和didSet观察程序。仅当在初始化上下文之外设置属性值时,才会调用它们
所以没有办法将struct与异步API调用@Code一起使用?因为我更喜欢使用结构而不是类。@Paul(再次)编辑了我的文章。是的,这是一个糟糕的设计(在这种情况下,我应该使用类。谢谢@Code。尽管它的设计很糟糕,但使用类编写单元测试要容易得多。(模仿struct的方法确实是一项令人沮丧的任务)是的,我知道。但我更喜欢使用didSet观察器,而不是将委托或回调与fetchData函数结合使用。所以我使用struct而不是class。如果无法将struct与异步变异函数结合使用,我应该使用class。
viewModel.data in didSet : 0
viewModel.data before fetch : 0
viewModel.data in fetchResponse : 10
viewModel.data after fetch : 10