Как выйти из Runloop?
Итак, у меня есть программа командной строки Swift:
import Foundation
print("start")
startAsyncNetworkingStuff()
RunLoop.current.run()
print("end")
Код компилируется без ошибок. Асинхронный сетевой код работает нормально, извлекает все его данные, печатает результат и в конечном итоге вызывает функцию завершения.
Как заставить эту функцию завершения вырваться из текущего цикла выполнения, чтобы был напечатан последний "конец"?
Добавлено:
Замена RunLoop.current.run() следующим:
print("start")
var shouldKeepRunning = true
startAsyncNetworkingStuff()
let runLoop = RunLoop.current
while ( shouldKeepRunning
&& runLoop.run(mode: .defaultRunLoopMode,
before: .distantFuture ) ) {
}
print("end")
настройка
shouldKeepRunning = false
в асинхронной сети функция завершения по-прежнему не приводит к печати "конца". (Это было проверено заключением в скобки оператора shouldKeepRunning = false с инструкциями печати, которые фактически выводят на консоль). Чего не хватает?
3 ответа
Для интерфейса командной строки используйте этот шаблон и добавьте обработчик завершения в AsyncNetworkingStuff (спасибо Робу за улучшение кода):
print("start")
let runLoop = CFRunLoopGetCurrent()
startAsyncNetworkingStuff() { result in
CFRunLoopStop(runLoop)
}
CFRunLoopRun()
print("end")
exit(EXIT_SUCCESS)
Пожалуйста, не используйте уродливые while
петли.
Вот как пользоваться URLSession
в инструменте командной строки macOS с использованием Swift 4.2
// Mac Command Line Tool with Async Wait and Exit
import Cocoa
// Store a reference to the current run loop
let runLoop = CFRunLoopGetCurrent()
// Create a background task to load a webpage
let url = URL(string: "http://SuperEasyApps.com")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data {
print("Loaded: \(data) bytes")
}
// Stop the saved run loop
CFRunLoopStop(runLoop)
}
task.resume()
// Start run loop after work has been started
print("start")
CFRunLoopRun()
print("end") // End will print after the run loop is stopped
// If your command line tool finished return success,
// otherwise return EXIT_FAILURE
exit(EXIT_SUCCESS)
Вам придется вызывать функцию остановки, используя ссылку на цикл выполнения до того, как вы начали (как показано выше), или используя GCD, чтобы выйти, как вы ожидаете.
func stopRunLoop() {
DispatchQueue.main.async {
CFRunLoopStop(CFRunLoopGetCurrent())
}
}
Рекомендации
https://developer.apple.com/documentation/corefoundation/1542011-cfrunlooprun
Циклы выполнения могут выполняться рекурсивно. Вы можете вызывать CFRunLoopRun() из любого обратного вызова цикла выполнения и создавать активацию вложенного цикла выполнения в стеке вызовов текущего потока.
https://developer.apple.com/documentation/corefoundation/1541796-cfrunloopstop
Если цикл выполнения вложен с выноской из одной активации, запускающей другую активацию, то выполняется только самая внутренняя активация.
(Отвечая на мой собственный вопрос)
Добавление следующего фрагмента в мой код завершения асинхронной сети позволяет печатать "конец":
DispatchQueue.main.async {
shouldKeepRunning = false
}