从 Swift 命令行程序中使用 NSURLSession

Posted

技术标签:

【中文标题】从 Swift 命令行程序中使用 NSURLSession【英文标题】:Using NSURLSession from a Swift command line program 【发布时间】:2015-06-08 06:21:02 【问题描述】:

我正在尝试在将其集成到更大的应用程序之前测试一个小的概念验证命令行应用程序。我要做的是使用 NSURLSession 使用this example 下载一些数据。但是,如果我使用简单的 OS X 命令行应用程序中给出的示例,那么应用程序会在检索数据之前退出。

如何使用 NSURLSession 从独立的命令行应用程序下载数据?我读到的是使用NSRunLoop 但是我还没有在 Swift 中找到一个明确的例子,所以如果 NSRunLoop 实际上是要走的路,那么任何例子都会受到赞赏。

也欢迎任何其他从 Swift 命令行应用程序的 URL 下载数据的策略(无限 while 循环?)。

【问题讨论】:

尝试在文件末尾添加NSRunLoop.mainRunLoop().run()... 谢谢,这行得通。那么,要确认一下,它会一直运行到程序在外部终止?您可以将其添加为答案,任何额外的信息将不胜感激。例如 a) 如何以编程方式退出 b) 以及一些关于为什么它比无限 while 循环更好的背景信息。 是的——它将永远运行。 我添加了一个答案,我会添加一些细节 好的——看起来你根本不需要运行循环。你可以使用 GCD 信号量。 【参考方案1】:

您可以使用信号量来阻止当前线程并等待您的 URL 会话完成。

创建信号量,启动 URL 会话,然后等待信号量。从您的 URL 会话完成回调中,发出信号量信号。

您可以使用全局标志(声明一个 volatile 布尔变量)并从 while 循环中轮询它,但这不是最优的。一方面,您正在不必要地消耗 CPU 周期。

这是我使用游乐场做的一个简单示例:

import Foundation

var sema = DispatchSemaphore( value: 0 )

class Delegate : NSObject, URLSessionDataDelegate

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
    
        print("got data \(String(data: data, encoding: .utf8 ) ?? "<empty>")");
        sema.signal()
    


let config = URLSessionConfiguration.default
let session = URLSession(configuration: config, delegate: Delegate(), delegateQueue: nil )

guard let url = URL( string:"http://apple.com" ) else  fatalError("Could not create URL object") 

session.dataTask( with: url ).resume()    

sema.wait()

【讨论】:

这是一个非常好的解决方案。确实需要一个围绕这个想法的框架来跟踪您将根据需要创建的所有信号量。 可能一旦 Swift 获得对“异步”类型特性的原生支持,就不再需要了。 如果您需要高级解决方案,请查看NSOperationQueueNSBlockOperation @nielsbot - 如果你有时间/有兴趣 - 你介意为 Swift 3 更新该解决方案吗? 它不燃烧循环。它使主线程休眠,直到内核将其唤醒以处理事件:例如计时器触发或按键被按下。【参考方案2】:

试试这个

let sema = DispatchSemaphore( value: 0)

let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg")!;

let task = URLSession.shared.dataTask(with: url)  (data, response, error) in
  print("after image is downloaded");
  sema.signal(); // signals the process to continue
;

task.resume();
sema.wait(); // sets the process to wait

【讨论】:

【参考方案3】:

出于概念验证或试用/测试目的,您可以通过硬编码一些超时时间来简化异步复杂性,直到您的工作完成。 (见下面的注释)

SWIFT 5

    //...your magic here
    // add a little ?iness to make it fun at least...
    RunLoop.main.run(until: Date() + 0x10)  //oh boi, default init && hex craze ?
    // yeah, 16 seconds timeout

    // or even worse (!)
    RunLoop.main.run(until: .distantFuture)

SWIFT 3 或更早版本

    //...your stuff here
    RunLoop.main.run(until: Date(timeIntervalSinceNow: 15))  //will execute things on main loop for 15 seconds

注意事项

    不要在生产中使用它 遵守第一条规则

这是一种非常快速而肮脏的方法来克服严重的并行问题。探索此问题的其他答案中描述的更好、更复杂的解决方案。

【讨论】:

sleep(15) 可以做同样的事情吗? (***.com/a/40870288/1135503重新回复) @KentLiau:你假设 C sleep() 函数吗?如果是这样,您需要导入 Darwin 模块,但请注意您正在运行代码的底层操作系统。

以上是关于从 Swift 命令行程序中使用 NSURLSession的主要内容,如果未能解决你的问题,请参考以下文章

使用 swift 2.3 从 xcode 8 中的命令行运行 xcode ui 测试

如何从 OSX Swift 命令行工具或 shebang 脚本文件显示窗口?

如何在 Swift 的操场上接受用户输入? [关闭]

需要一种用户输入类型的方法:命令行程序的双精度[重复]

Swift 命令行工具未接收 DistributedNotificationCenter 通知

无法使用 SQLite.swift 构建命令行项目