Swift 如何退出运行循环?

Swift 如何退出运行循环?,swift,swift3,Swift,Swift3,因此,我有一个Swift命令行程序: import Foundation print("start") startAsyncNetworkingStuff() RunLoop.current.run() print("end") 代码编译无误。异步网络代码运行正常,获取所有数据,打印结果,并最终调用其完成函数 如何使完成函数脱离当前运行循环,以便打印最后一个“结束” 增加: 将RunLoop.current.run()替换为以下内容: print("start") var shoul

因此,我有一个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语句与实际打印到控制台的打印语句放在括号中进行检查的)。缺少什么?

对于命令行界面,请使用此模式并将完成处理程序添加到AsyncNetworkingStaff(感谢Rob对代码的改进):

请不要在循环时使用丑陋的

(回答我自己的问题)

将以下代码段添加到我的异步网络完成代码中可以打印“end”:

DispatchQueue.main.async {
    shouldKeepRunning = false
}

下面介绍如何使用Swift 4.2在macOS命令行工具中使用
URLSession

// 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)
在开始之前(如上所示),您必须使用对run循环的引用来调用stop函数,或者使用GCD来按预期退出

func stopRunLoop() {
    DispatchQueue.main.async {
        CFRunLoopStop(CFRunLoopGetCurrent())
    }
}
参考资料

运行循环可以递归运行。您可以从任何运行循环调用中调用CFRunLoopRun(),并在当前线程的调用堆栈上创建嵌套的运行循环激活

如果运行循环嵌套了从一个激活开始运行另一个激活的调用,则仅退出最内部的激活


在中,他们说“如果你想终止运行循环,你不应该使用这个[
run
]方法。相反,使用另一个
run
方法,并在循环中检查你自己的其他任意条件。”后面的例子在Objective-C中,但它说明了这个想法。请参阅问题的更新,按照其他建议的运行方法。“最终”结果没有变化。丑陋?但苹果的官方文件中建议:丑陋。我猜(Objective-C)文档在引入blocks之前就已经编写好了(而且从未修改过);-)半途而废。在计时器回调函数中调用CFRunLoopStop(CFRunLoopGetCurrent())有效(“end”打印)。但是在NSURLSession dataTask完成块中调用CFRunLoopStop(CFRunLoopGetCurrent())似乎并不能停止CFRunLoop(“end”永远不会打印)。我在CLI中对URLSession使用的正是这段代码@取消停止运行循环和调用退出的冗余可能是,可能只是将CLI保留为特定状态的做法。@vadian。我同意。那个while loop很难看。不需要使用括号。看起来我的原始代码在异步网络线程和主runloop线程之间的线程间通信中有一个线程错误。下面是我使用解决方案2的最终工作代码:将URLSession API与完成处理程序一起使用比协议/委托更容易。@vadian:示例对于长时间运行的数据任务?(这是针对来自速度非常慢的服务器的数据,数据会在几秒钟内缓慢下降。)这没关系。委托方法仅在需要处理凭据或显示进度条时才有用。
func stopRunLoop() {
    DispatchQueue.main.async {
        CFRunLoopStop(CFRunLoopGetCurrent())
    }
}