Playground'da Eşzamansız geri aramaları nasıl çalıştırırım


117

Birçok Cocoa ve CocoaTouch yönteminde, Swift'de Objective-C ve Closures'da bloklar olarak uygulanan tamamlama geri aramaları bulunur. Ancak, bunları Playground'da denerken, tamamlama asla çağrılmaz. Örneğin:

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

Oyun alanı zaman çizelgemde konsol çıktısını görebiliyorum, ancak printlntamamlama bloğumdaki hiçbir zaman çağrılmıyor ...

Yanıtlar:


186

Bir çalıştırma döngüsünü manuel olarak çalıştırabilirsiniz (veya bir çalıştırma döngüsü gerektirmeyen eşzamansız kod için, semafor gönderme gibi diğer bekleme yöntemlerini kullanın), oyun alanlarında zaman uyumsuz çalışmayı beklemek için sağladığımız "yerleşik" yol, XCPlaygroundçerçeveyi içe aktarın ve ayarlayın XCPlaygroundPage.currentPage.needsIndefiniteExecution = true. Bu özellik ayarlanmışsa, en üst düzey oyun alanı kaynağınız bittiğinde oyun alanını durdurmak yerine orada ana çalıştırma döngüsünü döndürmeye devam edeceğiz, böylece eşzamansız kodun çalışma şansı olur. Oyun alanını, varsayılan olarak 30 saniye olan bir zaman aşımından sonra sona erdireceğiz, ancak bu, yardımcı düzenleyiciyi açıp zaman çizelgesi asistanını gösterirseniz yapılandırılabilir; zaman aşımı sağ alt taraftadır.

Örneğin, Swift 3'te ( URLSessionyerine kullanarak 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

Veya Swift 2'de:

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
Her şeye rağmen, Bu WWDC 2014 §408: Swift Playgrounds'da ele alınmıştır, ikinci yarı
Chris Conover

3
DP4'ten XCPlaygroundçerçevenin artık iOS Playgrounds için de mevcut olduğunu belirtmek gerekir .
ikuramedia

4
Güncellenen yöntem:XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
R Menke

23
Güncellenen yöntem: import PlaygroundSupportvePlaygroundPage.current.needsIndefiniteExecution = true
SimplGy

48

Bu API, Xcode 8'de tekrar değiştirildi ve şu adrese taşındı PlaygroundSupport:

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

Bu değişiklik WWDC 2016'da Oturum 213'te belirtildi .


2
Aramayı unutma PlaygroundPage.current.finishExecution().
Glenn

36

XCode 7.1 itibariyle XCPSetExecutionShouldContinueIndefinitely()kullanımdan kaldırılmıştır. Bunu şimdi yapmanın doğru yolu, ilk olarak geçerli sayfanın bir özelliği olarak belirsiz yürütme talep etmektir:

import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

… Daha sonra uygulamanın ne zaman bittiğini belirtin:

XCPlaygroundPage.currentPage.finishExecution()

Örneğin:

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

Geri aramaların çağrılmamasının nedeni, RunLoop'un Playground'da (veya bu konuda REPL modunda) çalışmamasıdır.

Geri aramaların işlemesini sağlamanın biraz sarsıntılı, ancak etkili bir yolu bir bayrakla ve ardından runloop'ta manuel olarak yinelemektir:

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

Bu model, zaman uyumsuz geri aramaları test etmesi gereken Birim Testlerinde sıklıkla kullanılmıştır, örneğin: Tamamlandığında ana kuyruğu çağıran birim testi zaman uyumsuz kuyruğu için model


8

XCode8, Swift3 ve iOS 10'daki gibi yeni API'ler,

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

Notlar:

Derleyiciye oyun alanı dosyasının "belirsiz yürütme" gerektirdiğini söyleyin

PlaygroundSupport.current.completeExecution()Tamamlama işleyiciniz içinde bir çağrı aracılığıyla yürütmeyi manuel olarak sonlandırın .

Önbellek dizini ile ilgili sorunlarla karşılaşabilirsiniz ve bunu çözmek için UICache.shared tekil dosyasını manuel olarak yeniden başlatmanız gerekir.

Misal:

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()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.