异步回调中的 Inout 参数未按预期工作 [英] Inout parameter in async callback does not work as expected

查看:45
本文介绍了异步回调中的 Inout 参数未按预期工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 inout 参数插入函数,以将从异步回调接收到的数据附加到外部数组.但是,它不起作用.我尝试了我所知道的一切来找出原因 - 没有运气.

I'm trying to insert functions with inout parameter to append data received from async callback to an outside array. However, it does not work. And I tried everything I know to find out why - with no luck.

根据@AirspeedVelocity 的建议,我将代码重写如下以删除不必要的依赖项.我还使用 Int 作为 inout 参数以保持简单.
输出始终为:
c 之前:0
c after: 1

As advised by @AirspeedVelocity, I rewrote the code as follows to remove unnecessary dependencies. I also use an Int as the inout parameter to keep it simple.
The output is always:
c before: 0
c after: 1

我无法弄清楚这里出了什么问题.

I'm not able to figure out what goes wrong here.

func getUsers() {
    let u = ["bane", "LiweiZ", "rdtsc", "ssivark", "sparkzilla", "Wogef"]
    var a = UserData()
    a.userIds = u
    a.dataProcessor()
}

struct UserData {
    var userIds = [String]()
    var counter = 0
    mutating func dataProcessor() -> () {
        println("counter: \(counter)")
        for uId in userIds {
            getOneUserApiData(uriBase + "user/" + uId + ".json", &counter)
        }
    }
}

func getOneUserApiData(path: String, inout c: Int) {
    var req = NSURLRequest(URL: NSURL(string: path)!)
    var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
    var session = NSURLSession(configuration: config)
    var task = session.dataTaskWithRequest(req) {
        (data: NSData!, res: NSURLResponse!, err: NSError!) in
        println("c before: \(c)")
        c++
        println("c after: \(c)")
        println("thread on: \(NSThread.currentThread())")
    }

    task.resume()
}

谢谢.

推荐答案

不幸的是,修改<代码> INOUT 参数在异步回调是无意义的.

Sad to say, modifying inout parameter in async-callback is meaningless.

来自官方文档:

参数可以提供默认值以简化函数调用,并且可以作为输入输出参数传递,一旦函数执行完毕就修改传递的变量.

Parameters can provide default values to simplify function calls and can be passed as in-out parameters, which modify a passed variable once the function has completed its execution.

...

一个in-out参数有一个值,它传入函数,被函数修改,并传回出函数来代替原来的值.

An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value.

从语义上讲,输入输出参数不是call-by-reference",但是call-by-copy-restore".

Semantically, in-out parameter is not "call-by-reference", but "call-by-copy-restore".

在您的情况下,counter 仅在 getOneUserApiData() 返回时回写,而不是在 dataTaskWithRequest() 回调中.

In your case, counter is write-backed only when getOneUserApiData() returns, not in dataTaskWithRequest() callback.

这是您的代码中发生的事情

Here is what happened in your code

  1. getOneUserApiData()调用时,counter 0的值被复制到c1
  2. 闭包捕获c1
  3. 调用dataTaskWithRequest()
  4. getOneUserApiData 返回,并且 - 未修改 - c1 的值被回写到 counter
  5. c2c3c重复1-4个过程>4 ...
  6. ...从互联网上获取...
  7. 回调被调用,c1 递增.
  8. 回调被调用,c2 递增.
  9. 回调被调用,c3 递增.
  10. 回调被调用,c4 递增.
  11. ...
  1. at getOneUserApiData() call, the value of counter 0 copied to c1
  2. the closure captures c1
  3. call dataTaskWithRequest()
  4. getOneUserApiData returns, and the value of - unmodified - c1 is write-backed to counter
  5. repeat 1-4 procedure for c2, c3, c4 ...
  6. ... fetching from the Internet ...
  7. callback is called and c1 is incremented.
  8. callback is called and c2 is incremented.
  9. callback is called and c3 is incremented.
  10. callback is called and c4 is incremented.
  11. ...

结果 counter 没有被修改:(

As a result counter is unmodified :(

详细说明

通常情况下,in-out 参数是通过引用传递的,但这只是编译器优化的结果.当闭包捕获 inout 参数时,pass-by-reference"不安全,因为编译器无法保证原始值的生命周期.例如,考虑以下代码:

Normally, in-out parameter is passed by reference, but it's just a result of compiler optimization. When closure captures inout parameter, "pass-by-reference" is not safe, because the compiler cannot guarantee the lifetime of the original value. For example, consider the following code:

func foo() -> () -> Void {
    var i = 0
    return bar(&i)
}

func bar(inout x:Int) -> () -> Void {
    return {
        x++
        return
    }
}

let closure = foo()
closure()

在这段代码中,当 foo() 返回时,var i 被释放.如果 x 是对 i 的引用,则 x++ 会导致访问冲突.为了防止这种竞争条件,Swift 采用了call-by-copy-restore".策略在这里.

In this code, var i is freed when foo() returns. If x is a reference to i, x++ causes access violation. To prevent such race condition, Swift adopts "call-by-copy-restore" strategy here.

这篇关于异步回调中的 Inout 参数未按预期工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆