在异步块内改变输入函数参数

Mutate inout function paramter inside async block

我有以下 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: []

我的问题是,为什么localArraddItem里面的self.arr不一样,和外面的a.arr不一样?我的期望是,当我将参数作为 inout 传递时,我应该能够对实际对象而不是副本进行操作,但显然这不是发生的情况。

编辑:感谢 dfri 的回答,我知道为什么这不起作用。 inout 实际上是 call-by-copy-restore, check another answer 。现在,关于如何实际制作闭包以捕获对原始对象的引用的任何建议?或者我应该使用其他技术来实现我想要的?

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

Do not depend on the behavioral differences between copy-in copy-out and call by reference.

...

When the function returns, your changes to the original are overwritten with the value of the copy. Do not depend on the implementation of the call-by-reference optimization to try to keep the changes from being overwritten.

现在,您的 addItem 函数将立即完成其调用,从而在函数中的延迟调度之前完成 inout copy-in/copy-out 任务。这使得在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

输出相当self-explanatory:

1: 0x00007fe19271e930
2: 0x00007fe19271e930
foo call to a finished, a = [1] <-- call to foo finished, 'inout' procedure complete
3: 0x00007fe19271e930           <-- dispatch in `foo` starts
4: 0x00007fe1927085e0           <-- mutates 'bar': 'bar' copied (and never 
                                    "returned" as this step is already finished)
value of a naturally not changed here = [1]
5: 0x00007fe19271e930           <-- naturally 'a' wont see the effect of the
                                    delayed mutation in foo

Swift 数组是值类型。总是在分配时复制。 如果要引用原始数组,请使用 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}"
)