121

多くの Cocoa および CocoaTouch メソッドには、Objective-C ではブロックとして、Swift では Closures として実装された完了コールバックがあります。ただし、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、完了ブロックでは呼び出されません...

4

8 に答える 8

193

実行ループを手動で実行できますが (または、実行ループを必要としない非同期コードの場合は、ディスパッチ セマフォなどの他の待機メソッドを使用します)、非同期作業を待機するためにプレイグラウンドで提供する "組み込み" の方法は、フレームワークをインポートして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
于 2014-06-05T17:17:36.477 に答える
16

コールバックが呼び出されない理由は、RunLoop が Playground (または REPL モード) で実行されていないためです。

コールバックを動作させるややぎこちない、しかし効果的な方法は、フラグを使用してから実行ループを手動で反復することです。

// 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)
}

このパターンは、非同期コールバックをテストする必要がある単体テストでよく使用されます。例:完了時にメイン キューを呼び出す非同期キューを単体テストするためのパターン

于 2014-06-05T10:55:42.807 に答える
10

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()
于 2016-11-25T10:58:58.017 に答える