문제

많은 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 내 완료 블록에서는 호출되지 않습니다 ...

도움이 되었습니까?

해결책

런 루프를 수동으로 실행할 수 있지만(또는 런 루프가 필요하지 않은 비동기 코드의 경우 디스패치 세마포어와 같은 다른 대기 방법을 사용) 비동기 작업을 기다리기 위해 플레이그라운드에 제공하는 "내장" 방식은 다음과 같습니다. 수입하다 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

다른 팁

이 API는 Xcode 8에서 다시 변경되었으며 PlaygroundSupport:

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

이 변경 사항은 다음에서 언급되었습니다. WWDC 2016 세션 213.

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()

콜백이 호출되지 않는 이유는 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)
}

이 패턴은 비동기 콜백을 테스트해야 하는 단위 테스트에서 자주 사용되었습니다. 예를 들면 다음과 같습니다. 완료 시 기본 대기열을 호출하는 단위 테스트 비동기 대기열의 패턴

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()

스위프트 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()

스위프트 3, xcode 8, iOS 10

노트:

플레이그라운드 파일에 "무한 실행"이 필요하다고 컴파일러에 알립니다.

호출을 통해 수동으로 실행을 종료합니다. PlaygroundSupport.current.completeExecution() 완료 처리기 내에서.

캐시 디렉터리에 문제가 발생할 수 있으며 이 문제를 해결하려면 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()
NSURLConnection.sendAsynchronousRequest(...)    
NSRunLoop.currentRunLoop().run()
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top