Варианты отсрочки или времени для ваших действий

В мире компьютерного программирования вы наверняка столкнетесь с гоночной опасностью. Опасность гонки возникает, когда две параллельные части кода завершаются в неправильном порядке. Это может быть не критическая ошибка, но это может быть критическим требованием. Мы все были в этом: ваш клиент хочет, чтобы что-то произошло, но он этого не делает. Они хотят, чтобы они выполнялись с определенным изяществом, в определенном порядке и в определенное время. В частности, в SwiftUI вам нужно замедлить работу приложения здесь и там, чтобы убедиться, что код в блоке A завершается раньше, чем код в блоке B.

В этом случае представьте, что нашим клиентом является Apple, и они хотят, чтобы вы помогли им составить презентацию WWDC по алгоритмам сортировки. Но он должен быть графическим. Вам нужно наглядно показать в коде, как это работает. Посмотрите эту презентацию WWDC, чтобы получить лучшее представление.

Так как вы это делаете? Чтобы было ясно, я не собираюсь показывать вам, как написать алгоритм сортировки. Это средство для достижения цели. Средство, чтобы проиллюстрировать, какие типы вызовов и методов у вас есть в SwiftUI и Swift, чтобы немного замедлить работу и побудить их работать друг с другом.

С чего начать? Я создал очень простое приложение, которое отображает восемь прямоугольников с числами внутри них. Коробки с разноцветным фоном. Ящики представляют собой массив чисел, которые нам нужно отсортировать. Но, как я сказал ранее, нам нужно делать это визуально.

Это хорошая проблема для работы, потому что она хорошо решена и понятна, поэтому у вас есть много вариантов. Я пошел по этому пути: я создал структуру, в которой собираюсь хранить несколько свойств. Выглядит это так:

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

Вернемся к брифу. Нам нужно показать сортировку в действии, поэтому нужно делать это медленно. Мне нужно заставить сортировку работать со скоростью человека. При этом мне нужно, чтобы это выглядело интересно. Что мне делать? Я начинаю с того, что откладываю первоначальную казнь. Я делаю это с помощью этого замечательного небольшого расширения, которое я нашел на SO:

func delay(_ delay:Double, closure:@escaping ()->()) {
  DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}

Здесь используется первое имеющееся в нашем арсенале оружие массовой задержки, известное как GCD. Он существует с iOS 4.0 и по большей части может использоваться практически везде и везде.

Следующий фрагмент кода для управления временем выполнения, который я использую, - это планировщик, который младше GCD. Он существует с iOS 10.0, и, честно говоря, я думаю, что он уже будет устаревшим в следующих выпусках и заменен фреймворком Combine. В отличие от GCD, у него есть один большой недостаток, о котором я могу думать прямо сейчас: планировщик должен работать в основном потоке (что нормально), но вы можете захотеть / должны объединить его с GCD, чтобы убедиться, что вы не блокируете что-либо. .

Строка для запуска нашего кода в оговоренное время выглядит так:

let timer = Timer.scheduledTimer(withTimeInterval: 1.2, repeats: true) { timer in
// code contained within a block
}

Таймер, который будет работать вечно или до тех пор, пока вы не отмените его с помощью такой строки:

timer.invalidate()

Далее идет структура Combine, с помощью которой мы определяем еще один таймер. Синтаксис для его работы выглядит следующим образом. Он предлагает явное преимущество перед планировщиком в том, что его «тики» в этом случае могут быть обнаружены внутри самого кода SwiftUI или с помощью Swift. Combine присоединился к шоу одновременно с SwiftUI, поэтому ему нужна iOS 13.0.

let next = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect()

Примеры обоих вариантов использования можно найти в демонстрационном приложении. Мы фиксируем «следующее» в приведенном выше случае в SwiftUI с помощью этого кода в SwiftUI:

.onReceive(next) { ( bind ) in
  tapGesture(box2D: bind)
}

И с этим кодом для функции в Swift, которую мы затем запускаем в очереди GCD:

Чтобы отменить подписку, как только вы ее начнете, вам нужно использовать эту команду. Да, я тоже думаю, что это немного похоже на взлом:

next.upstream.connect().cancel()

Последний фрагмент головоломки - это просто фрагменты анимации, характерные для SwiftUI. Я включил явную задержку. В данном случае 0,05 секунды. Очевидно, это также зависит от iOS 13.0.

withAnimation(.linear(duration: 0.05)) {
 // code to execute
}

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

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

Я начал эту статью с неприятной ошибки, называемой «гоночная опасность», и существует высокоуровневый набор команд для работы только с ней, называемый «Операционные очереди». Иногда их сравнивают и противопоставляют НОД, поскольку они похожи на зверей, только чуть более утонченно. Я читал, что они построены на основе GCD, хотя кажется, что они предшествуют ему (доступно в iOS 2.0), поэтому я не уверен, что это действительно так.

Использование операций с iOS позволяет объединять задачи в теги и связывать их выполнение с элементами управления и балансами. С учетом сказанного, я хочу упомянуть и семафоры - продвинутые средства синхронизации блоков кода. Продвинутый как сложный в отладке. Будь осторожен.

На этом я действительно подошел к концу статьи. Надеюсь, вам понравилось читать его так же, как и мне, и, возможно, вы действительно кое-что узнали в процессе. Вот код анимированного GIF, который ведет к статье:

Сохраняйте спокойствие, продолжайте кодировать.