Я хотел бы протестировать / отобразить поведение в Swift Playgrounds, связанное с функцией, которая изменяет значения после задержки. Для простоты скажем, что он мутирует строку. Я знаю, что могу отложить выполнение обновления значения через DispatchQueue.main.asyncAfter
и что я могу засыпать текущий поток, используя usleep
или sleep
.
Однако, поскольку игровая площадка, по-видимому, работает в синхронном потоке , Я не вижу изменений после сна.
Вот пример того, что я хотел бы сделать:
var string = "original"
let delayS: TimeInterval = 0.100
let delayUS: useconds_t = useconds_t(delayS * 1_000_000)
func delayedUpdate(_ value: String) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayS) {
string = value
}
}
delayedUpdate("test2")
assert(string == "original")
usleep(delayUS)
print(string) // ❌ Prints "original"
assert(string == "test2") // ❌ Assertion failure. string is "original" here
delayedUpdate("test3")
assert(string == "test2") // ❌ Assertion failure. string is "original" here
usleep(delayUS)
print(string) // ❌ Prints "original"
assert(string == "test3") // ❌ Assertion failure. string is "original" here
delayedUpdate("test4")
assert(string == "test3") // ❌ Assertion failure. string is "original" here
usleep(delayUS)
print(string) // ❌ Prints "original"
assert(string == "test4") // ❌ Assertion failure. string is "original" here
Обратите внимание на все неудавшиеся утверждения, поскольку ничего на верхнем уровне не видит изменений в string
. Это похоже на проблему синхронного или асинхронного потока.
Я знаю, что могу исправить это, заменив usleep
на большее asyncAfter
:
delayedUpdate("test2")
assert(string == "original")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayS) {
print(string)
assert(string == "test2")
delayedUpdate("test3")
assert(string == "test2")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayS) {
print(string)
assert(string == "test3")
delayedUpdate("test4")
assert(string == "test3")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayS) {
print(string)
assert(string == "test4")
}
}
}
Однако это приводит к пирамиде гибели кода с отступом каждый раз, когда приложение откладывается. Это не так уж плохо с 3 уровнями, но если у меня будет большая игровая площадка, за этим будет действительно трудно следить.
Есть ли способ использовать что-то более близкое к первому стилю линейного программирования, который учитывает обновления, обновляемые после задержки?
Другое возможное решение - заключить каждую ссылку на string
в asyncAfter
:
delayedUpdate("test2")
assert(string == "original")
usleep(delayUS)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { print(string) }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test2") }
delayedUpdate("test3")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test2") }
usleep(delayUS)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { print(string) }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test3") }
delayedUpdate("test4")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test3") }
usleep(delayUS)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { print(string) }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test4") }
Однако это тоже не является предпочтительным, поскольку это также довольно беспорядочно и, вероятно, подвержено ошибкам, если они Для выполнения своей функции, например, выполнение полагается на предыдущее значение string
. Также требуется 0.001
или аналогичная коррекция, чтобы убедиться в отсутствии состояния гонки.
Как использовать стиль линейного программирования (например, с sleep
) на игровой площадке Swift, но чтобы значения, которые обновляются во время сна, правильно отображались строками после sleep
?