使用 Swift 的 Grand Central Dispatch 出现问题
Trouble with Grand Central Dispatch using Swift
我有以下函数,它的行为与我预期的不一样。
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 的值,并在 duration 中传递的秒数内完成。
然而,我得到的行为是 dispatch_after 闭包中的代码只打印最终值,它从不打印增量。
延迟按预期发生,但所有值的计算都好像 for 循环已经完成。第一个 println(step) 确实显示步长递增,但第二个只显示最终值。
我显然对这应该如何工作有误解。我原以为闭包中的代码会保留调用 dispatch_after 方法时存在的值,但它的行为就像它使用实际执行时的任何值一样。
如何在 for 循环的每次迭代中捕获值并使用它在闭包中执行代码?
您发送给 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
上进行保留
不同于捕获值的Objective-C块,Swift闭包捕获变量。这意味着 Objective-C 块会捕获 "step" 变量的 100 个不同值,而 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)
}
所以闭包以 { 开头,后跟可选的 [ 方括号中的捕获列表],后跟可选的 (arguments) -> result in,然后是代码。
顺便说一句,通过使用 Float 而不是 Double,您可以无缘无故地将精度降低到大约 7 位而不是 15 位。
我有以下函数,它的行为与我预期的不一样。
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 的值,并在 duration 中传递的秒数内完成。
然而,我得到的行为是 dispatch_after 闭包中的代码只打印最终值,它从不打印增量。
延迟按预期发生,但所有值的计算都好像 for 循环已经完成。第一个 println(step) 确实显示步长递增,但第二个只显示最终值。
我显然对这应该如何工作有误解。我原以为闭包中的代码会保留调用 dispatch_after 方法时存在的值,但它的行为就像它使用实际执行时的任何值一样。
如何在 for 循环的每次迭代中捕获值并使用它在闭包中执行代码?
您发送给 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
上进行保留
不同于捕获值的Objective-C块,Swift闭包捕获变量。这意味着 Objective-C 块会捕获 "step" 变量的 100 个不同值,而 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)
}
所以闭包以 { 开头,后跟可选的 [ 方括号中的捕获列表],后跟可选的 (arguments) -> result in,然后是代码。
顺便说一句,通过使用 Float 而不是 Double,您可以无缘无故地将精度降低到大约 7 位而不是 15 位。