Ios 类和结构闭包中的Swift可变结构的行为不同
我有一个类(a),它有一个结构变量(S)。在这个类的一个函数中,我调用结构变量上的一个变异函数,这个函数接受闭包。此闭包的主体检查结构变量的name属性 结构的变异函数依次调用某个类(B)的函数。这个类的函数再次接受闭包。在这个闭包的主体中,修改结构,即更改name属性,并调用由第一个类提供的闭包 当我们在检查结构的name属性的地方调用第一个类(A)闭包时,它从未更改 但是在步骤2中,如果我使用结构(C)而不是类B,我发现类a内部的闭包结构实际上发生了变化。代码如下:Ios 类和结构闭包中的Swift可变结构的行为不同,ios,swift,mvvm,mutable,swift-structs,Ios,Swift,Mvvm,Mutable,Swift Structs,我有一个类(a),它有一个结构变量(S)。在这个类的一个函数中,我调用结构变量上的一个变异函数,这个函数接受闭包。此闭包的主体检查结构变量的name属性 结构的变异函数依次调用某个类(B)的函数。这个类的函数再次接受闭包。在这个闭包的主体中,修改结构,即更改name属性,并调用由第一个类提供的闭包 当我们在检查结构的name属性的地方调用第一个类(A)闭包时,它从未更改 但是在步骤2中,如果我使用结构(C)而不是类B,我发现类a内部的闭包结构实际上发生了变化。代码如下: class Networ
class NetworkingClass {
func fetchDataOverNetwork(completion:()->()) {
// Fetch Data from netwrok and finally call the closure
completion()
}
}
struct NetworkingStruct {
func fetchDataOverNetwork(completion:()->()) {
// Fetch Data from netwrok and finally call the closure
completion()
}
}
struct ViewModelStruct {
/// Initial value
var data: String = "A"
/// Mutate itself in a closure called from a struct
mutating func changeFromStruct(completion:()->()) {
let networkingStruct = NetworkingStruct()
networkingStruct.fetchDataOverNetwork {
self.data = "B"
completion()
}
}
/// Mutate itself in a closure called from a class
mutating func changeFromClass(completion:()->()) {
let networkingClass = NetworkingClass()
networkingClass.fetchDataOverNetwork {
self.data = "C"
completion()
}
}
}
class ViewController {
var viewModel: ViewModelStruct = ViewModelStruct()
func changeViewModelStruct() {
print(viewModel.data)
/// This never changes self.viewModel inside closure, Why Not?
viewModel.changeFromClass {
print(self.viewModel.data)
}
/// This changes self.viewModel inside/outside closure, Why?
viewModel.changeFromStruct {
print(self.viewModel.data)
}
}
}
var c = ViewController()
c.changeViewModelStruct()
为什么会有这种不同的行为。我认为区别因素应该是我是为viewModel使用结构还是使用类。但在这里,这取决于网络是一个类还是一个结构,它独立于任何ViewController或ViewModel。有人能帮我理解这一点吗?这不是一个解决方案,但通过这段代码,我们可以看到
ViewController的
,viewModel.data
已为类和结构案例正确设置。不同的是,viewModel.changeFromClass
闭包捕获了一个过时的self.viewModel.data
。请特别注意,只有类的“3 self”打印错误。不是包裹它的“2个自我”和“4个自我”图案
我想我对我们在原始问题中的行为有所了解。我的理解来自于闭包中inout参数的行为 简短回答: 它与捕获值类型的闭包是转义还是非转义有关。要使此代码正常工作,请执行以下操作
class NetworkingClass {
func fetchDataOverNetwork(@nonescaping completion:()->()) {
// Fetch Data from netwrok and finally call the closure
completion()
}
}
长答案:
让我先介绍一下情况
inout参数用于更改函数范围外的值,如以下代码所示:
func changeOutsideValue(inout x: Int) {
closure = {x}
closure()
}
var x = 22
changeOutsideValue(&x)
print(x) // => 23
func changeOutsideValue(inout x: Int)->() -> () {
closure = {x}
return closure
}
var x = 22
let c= changeOutsideValue(&x)
print(x) // => 22
c()
print(x) // => 22
这里x作为inout参数传递给函数。此函数更改闭包中x的值,因此它在其范围之外更改。现在x的值是23。当我们使用引用类型时,我们都知道这种行为。但对于值类型,inout参数是按值传递的。这里x是函数中的传递值,标记为inout。在将x传递到此函数之前,将创建并传递x的副本。因此,在changeOutsideValue中,此副本被修改,而不是原始x。现在,当这个函数返回时,这个修改后的x拷贝会复制回原来的x。因此,我们看到x仅在函数返回时才在外部修改。实际上,它看到在更改inout参数之后,函数是否返回,即捕获x的闭包是转义类还是非转义类
当闭包是转义类型时,即它只捕获复制的值,但在函数返回之前,不会调用它。请看下面的代码:
func changeOutsideValue(inout x: Int) {
closure = {x}
closure()
}
var x = 22
changeOutsideValue(&x)
print(x) // => 23
func changeOutsideValue(inout x: Int)->() -> () {
closure = {x}
return closure
}
var x = 22
let c= changeOutsideValue(&x)
print(x) // => 22
c()
print(x) // => 22
此处函数在转义闭包中捕获x的副本,以供将来使用,并返回该闭包。因此,当函数返回时,它会将x的未更改副本写回x(值为22)。如果打印x,它仍然是22。如果调用返回的闭包,它会更改闭包内部的本地副本,并且永远不会将其复制到x外部,因此x外部仍然是22
因此,这完全取决于更改inout参数的闭包是转义类型还是非转义类型。如果它是不可逃避的,变化就在外面,如果它是逃避的,变化就不在外面。
回到我们最初的例子。这就是流程:
var c=ViewController()创建的相同,
所以它和c一样
func changeFromClass(completion:()->())
我们创建了一个网络类
实例并将闭包传递给fetchDataOverNetwork函数。通知
这里是changeFromClass函数的闭包
fetchDataOverNetwork采用的是转义类型,因为
changeFromClass不假设闭包传入
在changeFromClass之前是否调用fetchDataOverNetwork
返回/// ViewModelStruct
mutating func changeFromClass(completion:(ViewModelStruct)->()){
let networkingClass = NetworkingClass()
networkingClass.fetchDataOverNetwork {
self.data = "C"
self = ViewModelStruct(self.data)
completion(self)
}
}
/// ViewController
func changeViewModelStruct() {
viewModel.changeFromClass { changedViewModel in
self.viewModel = changedViewModel
print(self.viewModel.data)
}
}
/// ViewModelStruct
var viewModelChanged: ((ViewModelStruct) -> Void)?
mutating func changeFromClass(completion:()->()) {
let networkingClass = NetworkingClass()
networkingClass.fetchDataOverNetwork {
self.data = "C"
viewModelChanged(self)
completion(self)
}
}
/// ViewController
func viewDidLoad() {
viewModel = ViewModelStruct()
viewModel.viewModelChanged = { changedViewModel in
self.viewModel = changedViewModel
}
}
func changeViewModelStruct() {
viewModel.changeFromClass {
print(self.viewModel.data)
}
}
import Foundation
import XCPlayground
protocol ViewModel {
var delegate: ViewModelDelegate? { get set }
}
protocol ViewModelDelegate {
func viewModelDidUpdated(model: ViewModel)
}
struct ViewModelStruct: ViewModel {
var data: Int = 0
var delegate: ViewModelDelegate?
init() {
}
mutating func fetchData() {
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) {
result in
self.data = 20
self.delegate?.viewModelDidUpdated(self)
print("viewModel.data in fetchResponse : \(self.data)")
XCPlaygroundPage.currentPage.finishExecution()
}.resume()
}
}
protocol ViewModeling {
associatedtype Type
var viewModel: Type { get }
}
typealias ViewModelProvide = protocol<ViewModeling, ViewModelDelegate>
class ViewController: ViewModelProvide {
var viewModel = ViewModelStruct() {
didSet {
viewModel.delegate = self
print("ViewModel in didSet \(viewModel)")
}
}
func viewDidLoad() {
viewModel = ViewModelStruct()
}
func changeViewModelStruct() {
print(viewModel)
viewModel.fetchData()
}
}
extension ViewModelDelegate where Self: ViewController {
func viewModelDidUpdated(viewModel: ViewModel) {
self.viewModel = viewModel as! ViewModelStruct
}
}
var c = ViewController()
c.viewDidLoad()
c.changeViewModelStruct()