如何在Playground中运行asynchronouscallback

许多Cocoa和CocoaTouch方法在Objective-C和Swift中的闭包中都实现了完成callback。 但是,在Playground中尝试这些时,完成不会被调用。 例如:

// Playground - noun: a place where people can play import Cocoa import XCPlayground let url = NSURL(string: "http://stackoverflow.com") let request = NSURLRequest(URL: url) NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.currentQueue() { response, maybeData, error in // This block never gets called? if let data = maybeData { let contents = NSString(data:data, encoding:NSUTF8StringEncoding) println(contents) } else { println(error.localizedDescription) } } 

我可以看到我的Playground时间轴中的控制台输出,但是我的完成块中的println永远不会被调用…

虽然可以手动运行一个运行循环(或者,对于不需要运行循环的asynchronous代码,使用其他等待方法(如调度信号量)),但是我们在操场上提供的用于等待asynchronous工作的“内置”方式是导入XCPlayground框架并设置XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 。 如果已经设置了这个属性,那么当你的顶层游戏源代码完成的时候,我们将继续旋转主运行循环,所以asynchronous代码有机会运行。 超时后,我们最终会终止操场,默认为30秒,但如果您打开助理编辑器并显示时间线助手,则可以configuration它; 超时在右下angular。

不幸的是,这个开发者预览版不包括iOS的XCPlayground ; 只适用于OSX。

例子:( Swift V2)

 import UIKit import XCPlayground let url = NSURL(string: "http://stackoverflow.com") let request = NSURLRequest(URL: url!) NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.currentQueue()) { response, maybeData, error in if let data = maybeData { let contents = NSString(data:data, encoding:NSUTF8StringEncoding) println(contents) } else { println(error.localizedDescription) } } XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 

例子:( Swift V3)

 import UIKit import PlaygroundSupport let url = NSURL(string: "http://stackoverflow.com") let request = NSURLRequest(url: url as! URL) NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: OperationQueue.current!) { data, response, error in if let data = response { let contents = NSString(data:data, encoding:String.Encoding.utf8.rawValue) print(contents!) } else { print(error?.localizedDescription as Any) } } PlaygroundPage.current.needsIndefiniteExecution = true 

这个API在Xcode 8中再次发生了变化,并被移到了PlaygroundSupport

 import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true 

2016年WWDC会议上提到了这一变化。

从XCode 7.1起, XCPSetExecutionShouldContinueIndefinitely()已被弃用。 现在这样做的正确方法是首先请求作为当前页面的属性的不确定执行:

 import XCPlayground XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 

…然后指出何时执行完成:

 XCPlaygroundPage.currentPage.finishExecution() 

例如:

 import Foundation import XCPlayground XCPlaygroundPage.currentPage.needsIndefiniteExecution = true NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) { result in print("Got result: \(result)") XCPlaygroundPage.currentPage.finishExecution() }.resume() 

没有调用callback的原因是因为RunLoop没有在Playground中运行(或者在REPL模式下)。

一个有点令人反感,但有效的callback方式是使用一个标志,然后在runloop上手动迭代:

 // Playground - noun: a place where people can play import Cocoa import XCPlayground let url = NSURL(string: "http://stackoverflow.com") let request = NSURLRequest(URL: url) var waiting = true NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.currentQueue() { response, maybeData, error in waiting = false if let data = maybeData { let contents = NSString(data:data, encoding:NSUTF8StringEncoding) println(contents) } else { println(error.localizedDescription) } } while(waiting) { NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate()) usleep(10) } 

这种模式经常用于需要testingasynchronouscallback的unit testing ,例如: unit testingasynchronous队列模式,在完成时调用主队列

XCode8,Swift3和iOS 10的新API,

 // import the module import PlaygroundSupport // write this at the beginning PlaygroundPage.current.needsIndefiniteExecution = true // To finish execution PlaygroundPage.current.finishExecution() 

Swift 3,xcode 8,iOS 10

笔记:

告诉编译器,操场文件需要“无限期执行”

通过在完成处理程序中调用PlaygroundSupport.current.completeExecution()手动终止执行。

您可能会遇到caching目录的问题,并解决此问题,您将需要手动重新实例化UICache.shared单身人士。

例:

 import UIKit import Foundation import PlaygroundSupport // resolve path errors URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil) // identify that the current page requires "indefinite execution" PlaygroundPage.current.needsIndefiniteExecution = true // encapsulate execution completion func completeExecution() { PlaygroundPage.current.finishExecution() } let url = URL(string: "http://i.imgur.com/aWkpX3W.png") let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in var image = UIImage(data: data!) // complete execution completeExecution() } task.resume() 

Swift 4,Xcode 9.0

 import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")! let task = URLSession.shared.dataTask(with: url) { (data, response, error) in guard error == nil else { print(error?.localizedDescription ?? "") return } if let data = data, let contents = String(data: data, encoding: String.Encoding.utf8) { print(contents) } } task.resume() 
 NSURLConnection.sendAsynchronousRequest(...) NSRunLoop.currentRunLoop().run()