3

我想看看我是否可以为我的模型使用结构并正在尝试这个。当我打电话时 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
4

3 回答 3

4

inout参数不是对值类型的引用——它只是该值类型的影子副本,当函数返回时,它会写回调用者的值。

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

由于这种对inout参数的普遍误解,有一个 Swift Evolution 提议只允许闭包inout捕获参数。@noescape从 Swift 3 开始,您当前的代码将不再编译。

如果你真的需要在你的代码中传递引用——那么你应该使用引用类型(让你Model的 a class)。虽然我怀疑您可能能够重构您的逻辑以避免首先传递引用(但是没有看到您的实际代码,这是不可能的建议)。

编辑:自从发布此答案以来,inout参数现在可以编译为传递引用,可以通过查看发出的 SIL 或 IR 来查看。但是,由于没有保证调用者的值在函数调用后仍然有效。)

于 2016-05-05T09:54:00.197 回答
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
于 2016-05-05T08:57:14.717 回答
0

这是苹果公司对此的看法。

类和结构

值类型是在将其分配给变量或常量或将其传递给函数时复制的类型。[...] 所有结构和枚举都是 Swift 中的值类型

方法

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

但是,如果您需要在特定方法中修改结构或枚举的属性,您可以选择更改该方法的行为。然后,该方法可以从该方法内部改变(即更改)其属性,并且它所做的任何更改都会在该方法结束时写回原始结构。该方法还可以为其隐含的 self 属性分配一个全新的实例,并且该新实例将在方法结束时替换现有实例。

取自这里

于 2016-05-05T09:04:28.923 回答