Как выйти из 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
}
Другие вопросы по тегам