Làm cách nào để chạy lệnh gọi lại không đồng bộ trong Playground


117

Nhiều phương thức Cocoa và CocoaTouch có các lệnh gọi lại hoàn thành được thực hiện dưới dạng các khối trong Objective-C và Closures trong Swift. Tuy nhiên, khi thử những thứ này trong Playground, quá trình hoàn thành sẽ không bao giờ được gọi. Ví dụ:

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

Tôi có thể thấy đầu ra của bảng điều khiển trong dòng thời gian Playground của mình, nhưng printlntrong khối hoàn thành của tôi không bao giờ được gọi ...

Câu trả lời:


186

Mặc dù bạn có thể chạy vòng lặp chạy theo cách thủ công (hoặc, đối với mã không đồng bộ không yêu cầu vòng lặp chạy, hãy sử dụng các phương pháp chờ khác như semaphores điều phối), cách "tích hợp sẵn" mà chúng tôi cung cấp trong sân chơi để chờ hoạt động không đồng bộ là nhập XCPlaygroundkhuôn khổ và thiết lập XCPlaygroundPage.currentPage.needsIndefiniteExecution = true. Nếu thuộc tính này đã được đặt, khi nguồn sân chơi cấp cao nhất của bạn kết thúc, thay vì dừng sân chơi ở đó, chúng tôi sẽ tiếp tục quay vòng chạy chính để mã không đồng bộ có cơ hội chạy. Cuối cùng chúng tôi sẽ kết thúc sân chơi sau thời gian chờ mặc định là 30 giây, nhưng có thể được định cấu hình nếu bạn mở trình chỉnh sửa trợ lý và hiển thị trợ lý dòng thời gian; thời gian chờ ở phía dưới bên phải.

Ví dụ, trong Swift 3 (sử dụng URLSessionthay vì 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

Hoặc trong 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

1
Đối với tất cả giá trị của nó, Điều này được đề cập trong WWDC 2014 §408: Swift Playgrounds, nửa sau
Chris Conover

3
Đáng chú ý là từ DP4, XCPlaygroundkhung công tác hiện cũng có sẵn cho iOS Playgrounds.
ikuramedia

4
Phương pháp cập nhật:XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
R Menke

23
Phương pháp cập nhật: import PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = true
SimplGy

48

API này đã thay đổi một lần nữa trong Xcode 8 và nó đã được chuyển sang PlaygroundSupport:

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

Thay đổi này đã được đề cập trong Phiên 213 tại WWDC 2016 .


2
Đừng quên gọi điện PlaygroundPage.current.finishExecution().
Glenn

36

Kể từ XCode 7.1, XCPSetExecutionShouldContinueIndefinitely()không được dùng nữa. Cách chính xác để làm điều này bây giờ là trước tiên yêu cầu thực thi vô thời hạn như một thuộc tính của trang hiện tại:

import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

… Sau đó cho biết khi nào quá trình thực thi kết thúc với:

XCPlaygroundPage.currentPage.finishExecution()

Ví dụ:

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

16

Lý do gọi lại không được gọi là do RunLoop không chạy trong Playground (hoặc ở chế độ REPL cho vấn đề đó).

Một cách hơi buồn tẻ, nhưng hiệu quả, để làm cho các lệnh gọi lại hoạt động là gắn cờ và sau đó lặp lại thủ công trên 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)
}

Mẫu này thường được sử dụng trong Kiểm tra đơn vị cần kiểm tra lệnh gọi lại không đồng bộ, ví dụ: Mẫu cho hàng đợi kiểm tra đơn vị không đồng bộ gọi hàng đợi chính khi hoàn thành


8

Các API mới dành cho XCode8, Swift3 và iOS 10 là,

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

5

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

3

Swift 3, xcode 8, iOS 10

Ghi chú:

Cho trình biên dịch biết rằng tệp sân chơi yêu cầu "thực thi vô thời hạn"

Kết thúc thực thi theo cách thủ công thông qua lệnh gọi đến PlaygroundSupport.current.completeExecution()trong trình xử lý hoàn thành của bạn.

Bạn có thể gặp sự cố với thư mục bộ đệm và để giải quyết vấn đề này, bạn sẽ cần khởi tạo lại singleton UICache.shared theo cách thủ công.

Thí dụ:

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

-3
NSURLConnection.sendAsynchronousRequest(...)    
NSRunLoop.currentRunLoop().run()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.