2015-03-21 3 views
1

У меня есть следующая функция, которая не ведет себя так, как я ожидал.Проблема с Grand Central Dispatch с использованием 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 и закончить в число секунд, прошедших в срок.

Однако поведение, которое я получаю, заключается в том, что код в dispatch_after закрывает только окончательное значение, он никогда не печатает приращения.

Задержка происходит, как ожидалось, но значения рассчитываются так, как если бы цикл for уже был завершен. Первый println (шаг) показывает шаг приращения, но второй показывает только окончательное значение.

У меня явно есть непонимание того, как это должно работать. Я ожидал, что код в закрытии будет содержать значения, существовавшие в момент вызова метода dispatch_after, но он действует так, как будто он использует то, что значение находится во время выполнения на самом деле.

Как я могу захватить значения на каждой итерации цикла for и выполнить код в закрытии, используя это?

ответ

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) 
     } 
    } 
} 

Это будет работать, как это. Закрытие делает удержание на step, как объяснено на the swift reference.

+0

Большое спасибо! Это убило меня. – Scooter

+0

Мое предположение заключается в том, что 'step' получает коробку - в предположении, потому что' println() 'в лямбда принимает объект. Помните, что компилятор не знает, что 'println()' не изменяет 'step'. Я предполагаю, что альтернатива взятия неизменяемой копии по значению при захвате создаст не менее удивительные результаты ... – marko

+0

@marko компилятор должен знать, что, учитывая, что структура имеет семантику copy-on-assign, 'println' никогда не может изменять шаг – Juan

1

В отличие от блоков Objective-C, которые фиксируют значения, Swift замыкания захвата переменные. Это означает, что если блок Objective-C захватил бы 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) 
} 

Так замыкание начинается с {, а затем при необходимости в списке захвата в [квадратных скобках], а затем, возможно пути (аргументы) -> в результате, с последующим кодом.

BTW, используя Float вместо Double, вы уменьшаете точность до 7 цифр вместо 15 без каких-либо оснований.

Смежные вопросы