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

时间:2016-05-05 05:29:42

标签: swift struct closures mutating-function

我试图看看我是否可以为我的模型使用结构并尝试这个。当我致电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

3 个答案:

答案 0 :(得分:3)

inout参数不是对值类型的引用 - 它只是该值类型的卷影副本,它会被写回调用者&# 39;函数返回时的s值。

您的代码中发生的是inout变量正在逃避函数的生命周期(通过在随后存储的闭包中捕获) - 意味着{{1}的任何更改函数返回后的变量永远不会反映在该闭包之外。

由于这种关于inout参数的常见误解,been a Swift Evolution proposal只允许inout个句点捕获inout个参数。从Swift 3开始,您当前的代码将不再编译。

如果您确实需要传递代码中的引用 - 那么您应该使用引用类型(将@noescape设为Model)。虽然我怀疑你可能能够重构你的逻辑以避免在第一时间传递引用(但是没有看到你的实际代码,它是不可能建议的。)

编辑>自发布此答案以来,class参数现在可以编译为传递引用,可以通过查看发出的SIL或IR来查看。由于无法保证无论哪个在函数调用后调用者的值仍然有效,因此您无法对其进行处理。)

答案 1 :(得分:0)

闭包的实例将获得它自己的,独立的捕获值副本,只有它可以改变。在执行闭包时捕获该值。让我们看看你稍微修改过的代码

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

答案 2 :(得分:0)

这就是Apple所说的。

Classes and Structures

  

值类型是在分配给a时复制的类型   变量或常量,或传递给函数时。 [...]全部   结构和枚举是Swift中的值类型

Methods

结构和枚举是值类型。默认情况下,无法在其实例方法中修改值类型的属性。

  

但是,如果您需要修改结构的属性或   在特定方法中枚举,您可以选择进行变异   该方法的行为。然后该方法可以变异(即,   改变)方法中的属性,以及它的任何变化   当方法结束时,make会被写回原始结构。   该方法还可以为其隐式分配一个全新的实例   自我属性,这个新实例将取代现有的实例   当方法结束时。

取自here