许多Cocoa和CocoaTouch方法都有完成回调,实现为Objective-C中的块和Swift中的闭包.但是,在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
我的完成块永远不会被调用...
不调用回调的原因是因为RunLoop没有在Playground中运行(或者在REPL模式下运行).
使回调操作的一种有点笨拙但有效的方法是使用标志,然后在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) }
此模式通常用于需要测试异步回调的单元测试中,例如:用于单元测试的模式异步队列,在完成时调用主队列
从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()
此API在Xcode 8中再次更改,并且已移至PlaygroundSupport
:
import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true
2016年WWDC会议213中提到了这一变化.
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()
虽然您可以手动运行运行循环(或者,对于不需要运行循环的异步代码,使用其他等待方法,如调度信号量),我们在操场上提供的"内置"方式等待异步工作是导入XCPlayground
框架并设置XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
.如果已设置此属性,当您的顶级游乐场源完成时,我们将继续旋转主运行循环,而不是停止操场,因此异步代码有机会运行.我们最终会在超时后终止操场,默认为30秒,但如果您打开助理编辑器并显示时间线助手,则可以配置; 超时在右下方.
例如,在Swift 3中(使用URLSession
代替NSURLConnection
):
import UIKit import PlaygroundSupport let url = URL(string: "http://stackoverflow.com")! URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data, error == nil else { print(error ?? "Unknown error") return } let contents = String(data: data, encoding: .utf8) print(contents!) }.resume() PlaygroundPage.current.needsIndefiniteExecution = true
或者在Swift 2中:
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
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()