使用Swift解决Grand Central Dispatch问题

时间:2015-03-21 19:49:02

标签: ios swift grand-central-dispatch

我有以下功能,并不像我预期的那样。

func dispatchTrouble(startValue:Float, endValue:Float, duration:Float)
{
    //100 increment steps per second
    let incrementSteps = duration * 100

    for(var step = 0.0 as Float; step < incrementSteps; step++)
    {
        var delayInSeconds = step * (duration / incrementSteps)
        let answer = Float(NSEC_PER_SEC) * delayInSeconds
        let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(answer))
        println(step)

        //Using GCD to perform this on the main thread.
        dispatch_after(popTime, dispatch_get_main_queue()){
            println(step)
            let fraction = step / incrementSteps
            let incrementedValue = startValue + (endValue - startValue) * fraction
            println(incrementedValue)
        }
    }
}

我希望println(incrementedValue)语句显示一个从startValue增加到endValue的值,并以持续时间内传递的秒数结束。

然而,我得到的行为是dispatch_after闭包中的代码只打印最终值,它从不打印增量。

延迟按预期发生,但所有值都计算好像for循环已经完成。第一个println(步骤)显示步骤递增,但第二个只显示最终值。

我显然对这应该如何运作有误解。我预计闭包中的代码将保存调用dispatch_after方法时存在的值,但它的作用就像它使用的是实际执行时的值。

如何在for循环的每次迭代中捕获值并使用它来执行闭包中的代码?

2 个答案:

答案 0 :(得分:1)

您发送给GDC的所有闭包都指向相同的step变量。这意味着每当其中一个执行时,它就具有循环结束时的值。

尝试将代码更改为:

func dispatchTrouble(startValue:Float, endValue:Float, duration:Float)
{
    //100 increment steps per second
    let incrementSteps = duration * 100

    for(var step = 0.0 as Float; step < incrementSteps; step++)
    {
        var delayInSeconds = step * (duration / incrementSteps)
        let answer = Float(NSEC_PER_SEC) * delayInSeconds
        let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(answer))
        println(step)

        let stepCopy = step

        //Using GCD to perform this on the main thread.
        dispatch_after(popTime, dispatch_get_main_queue()){
            println(stepCopy)
            let fraction = stepCopy / incrementSteps
            let incrementedValue = startValue + (endValue - startValue) * fraction
            println(incrementedValue)
        }
    }
}

它会像那样工作。正如the swift reference所述,关闭正在step上保留。

答案 1 :(得分:1)

与捕获的Objective-C块不同,Swift闭包捕获变量。这意味着Objective-C块将捕获100个不同的“step”变量值,Swift闭包捕获变量本身并在调用闭包时打印其值。

解决此问题的最佳方法是添加捕获列表。

dispatch_after(popTime, dispatch_get_main_queue()){
    [let stepcopy = step] () -> Void in
    println(stepcopy)
    let fraction = stepcopy / incrementSteps
    let incrementedValue = startValue + (endValue - startValue) * fraction
    println(incrementedValue)
}

因此,闭包以{开头,后跟[括号]中的捕获列表,后跟可选的(参数) - &gt;结果,然后是代码。

通过使用Float而不是Double,BTW可以将精度降低到大约7位而不是15位,这是没有任何理由的。