在闭包中捕获结构引用不允许发生突变

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 取决于ViewModelModel 之间的关系是什么。 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

【讨论】:

以上是关于在闭包中捕获结构引用不允许发生突变的主要内容,如果未能解决你的问题,请参考以下文章

在闭包中引用属性需要明确的“自我”。使捕获语义明确

➽06闭包

Swift的闭包,枚举,类和结构体

scala 闭包的概念

闭包结构的本质

scala为什么要清理闭包