Nano Hash - криптовалюты, майнинг, программирование

Проблема с 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(step) показывает увеличение шага, а второй показывает только конечное значение.

Я явно неправильно понимаю, как это должно работать. Я ожидал, что код в замыкании будет содержать значения, существовавшие на момент вызова метода 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, как описано в быстрая ссылка.

21.03.2015
  • Огромное спасибо! Это убивало меня. 21.03.2015
  • Я предполагаю, что step попадает в коробку - скорее всего, потому что println() в лямбда-выражении принимает объект. Помните, компилятор не может знать, что println() не изменяет step. Я предполагаю, что альтернатива использованию неизменной копии по значению при захвате привела бы к столь же удивительным результатам... 22.03.2015
  • @marko компилятор должен знать, что, учитывая, что структура имеет семантику копирования при назначении, println никогда не сможет изменить шаг 22.03.2015

  • 2

    В отличие от блоков 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)
    }
    

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

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

    22.07.2015
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..