在闭包中捕获结构引用不允许发生突变
Posted
技术标签:
【中文标题】在闭包中捕获结构引用不允许发生突变【英文标题】:Capturing a struct reference in a closure doesn't allow mutations to occur 【发布时间】:2016-05-05 05:29:42 【问题描述】:我正在尝试查看是否可以为我的模型使用结构并尝试这样做。当我调用vm.testClosure()
时,它不会改变x
的值,我不知道为什么。
struct Model
var x = 10.0
var m = Model()
class ViewModel
let testClosure:() -> ()
init(inout model: Model)
testClosure =
() -> () in
model.x = 30.5
var vm = ViewModel(model:&m)
m.x
vm.testClosure()
m.x
【问题讨论】:
struct 不能通过引用***.com/questions/31495431/… 【参考方案1】:inout
参数不是对值类型的引用——它只是该值类型的影子副本,在函数返回时写回调用者的值。
您的代码中发生的情况是,您的 inout
变量正在逃避函数的生命周期(通过在随后存储的闭包中被捕获)——这意味着在函数返回后对 inout
变量的任何更改永远不会反映在该闭包之外。
由于对inout
参数的这种常见误解,been a Swift Evolution proposal 只允许inout
参数被@noescape
闭包捕获。从 Swift 3 开始,您当前的代码将不再编译。
如果您确实需要在代码中传递引用,那么您应该使用引用类型(将您的 Model
设为 class
)。虽然我怀疑您可能能够重构您的逻辑以避免首先传递引用(但是没有看到您的实际代码,这是不可能的建议)。
(编辑: 自发布此答案以来,inout
参数现在可以编译为传递引用,可以通过查看发出的 SIL 或 IR 来查看。但是你是由于无法保证 whatsoever 调用者的值在函数调用后仍将保持有效,因此无法这样对待它们。)
【讨论】:
好的,非常感谢,这很有意义。似乎不可能为模型使用结构,因为视图模型无法与模型交互?? @aland 取决于ViewModel
和Model
之间的关系是什么。 ViewModel
保留 Model
的值(带有属性)不是更有意义吗?这样你就可以在你的ViewModel
中改变模型——并通过属性从ViewModel
外部访问更改。您的代码的实际用例是什么?【参考方案2】:
闭包的实例将获得它们自己的、独立的捕获值的副本,并且只有它可以更改。该值是在执行闭包时捕获的。让我们看看你稍微修改过的代码
struct Model
var x = 10.0
mutating func modifyX(newValue: Double)
let this = self
let model = m
x = newValue
// put breakpoint here
//(lldb) po model
//▿ Model
// - x : 30.0
//
//(lldb) po self
//▿ Model
// - x : 301.0
//
//(lldb) po this
//▿ Model
// - x : 30.0
var m = Model()
class ViewModel
let testClosure:() -> ()
init(inout model: Model)
model.x = 50
testClosure =
() -> () in
model.modifyX(301)
model.x = 30
let mx = m.x
vm.testClosure()
let mx2 = m.x
【讨论】:
【参考方案3】:这是 Apple 的说法。
Classes and Structures
值类型是在分配给 变量或常量,或者当它被传递给函数时。 [...] 全部 结构和枚举是 Swift 中的值类型
Methods
结构和枚举是值类型。默认情况下,值类型的属性不能在其实例方法中修改。
但是,如果您需要修改结构的属性或 特定方法中的枚举,您可以选择变异 该方法的行为。然后该方法可以变异(即, 更改)它在方法内的属性,以及它的任何更改 当方法结束时,makes 被写回原始结构。 该方法还可以为其隐式分配一个全新的实例 self 属性,这个新实例将替换现有的 当方法结束时。
取自here
【讨论】:
以上是关于在闭包中捕获结构引用不允许发生突变的主要内容,如果未能解决你的问题,请参考以下文章