在异步块内突变函数参数

时间:2016-01-27 10:59:45

标签: ios swift closures swift-playground

我有以下游乐场代码:

import UIKit
import XCPlayground

class A {

    var arr : [UIImage] = []

    func addItem(inout localArr: [UIImage]) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(2 * NSEC_PER_SEC)), dispatch_get_main_queue()) { () -> Void in
            localArr.append(UIImage())
            print("from inside function localArr: \(localArr)")
            print("form inside function: \(self.arr)")
        }
    }
}

let a = A()
a.addItem(&a.arr)
print("instant print :\(a.arr)")

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(3 * NSEC_PER_SEC)), dispatch_get_main_queue()) { () -> Void in
    print("print after delay: \(a.arr)")
}

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

输出结果为:

instant print :[]
from inside function localArr: [<UIImage: 0x7f99e8706f10>, {0, 0}]
form inside function: []
print after delay: []

我的问题是,为什么localArrself.arr中的addItem不同,而a.arrinout不同?我的期望是,当我将参数作为inout传递时,我应该能够对实际对象进行操作,而不是副本,但显然这不是发生的事情。

编辑:感谢dfri回答,我知道为什么这不起作用。 k8s.io/kubernetes/pkg/api确实是call-by-copy-restore,请检查另一个答案here。现在,关于如何实际使闭包捕获对原始对象的引用的任何建议?或许我应该用其他技术来实现我想要的目标?

2 个答案:

答案 0 :(得分:2)

有关inout关键字的某些理论,请参阅以下答案:

  

不要依赖于copy-in copy-out之间的行为差​​异   并通过引用致电。

     

...

     

当函数返回时,您对原始文件的更改是   用副本的值覆盖。不要依赖   实现调用引用优化以试图保持   被覆盖的变化。

现在,您的addItem函数将立即完成其调用,从而在函数中延迟调度之前完成inout复制/复制输出任务。这使得在{/ 1}}参数的方法中中使用延迟调度本身就很糟糕,至少如果延迟是试图改变inout参数的延迟。

要看到这一点,我们可以跟踪某个数组的引用(而不是跟踪值),以及它们在我们的示例运行时期间如何显示数组的变异。

inout

输出很明显:

func foo(inout bar: [Int]) {
    var pBar : UnsafePointer<Int> = UnsafePointer(bar)
    print("2: \(pBar)")
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(2 * NSEC_PER_SEC)), dispatch_get_main_queue()) { () -> Void in
        pBar = UnsafePointer(bar)
        print("3: \(pBar)")
        bar[0] = 2
        pBar = UnsafePointer(bar)
        print("4: \(pBar)")
    }
}

var a : [Int] = [1]
var pA : UnsafePointer<Int> = UnsafePointer(a)
print("1: \(pA)")
foo(&a)
print("foo call to a finished, a = \(a)")

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(5 * NSEC_PER_SEC)), dispatch_get_main_queue()) { () -> Void in
    print("value of a naturally not changed here = \(a)")
    pA = UnsafePointer(a)
    print("5: \(pA)")
}

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

答案 1 :(得分:1)

Swift Arrays是值类型。总是复制分配。 如果要引用原始数组,请使用NSMutableArray。 那么也不需要inout

class A {

    var arr : NSMutableArray = []

    func addItem(localArr: NSMutableArray) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(2 * NSEC_PER_SEC)), dispatch_get_main_queue()) { () -> Void in
            localArr.addObject(UIImage())
            print("from inside function localArr: \(localArr)")
            print("form inside function: \(self.arr)")
        }
    }
}

let a = A()
a.addItem(a.arr)
print("instant print :\(a.arr)")

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(3 * NSEC_PER_SEC)), dispatch_get_main_queue()) { () -> Void in
    print("print after delay: \(a.arr)")
}

打印:

instant print :(
)
from inside function localArr: (
    "<UIImage: 0x7ffe1872b5f0>, {0, 0}"
)
form inside function: (
    "<UIImage: 0x7ffe1872b5f0>, {0, 0}"
)
print after delay: (
    "<UIImage: 0x7ffe1872b5f0>, {0, 0}"
)