Domanda

Molti metodi di cacao e cocoaouch hanno callback di completamento implementato come blocchi in obiettivi-C e chiusure in Swift.Tuttavia, quando si prova in gioco nel parco giochi, il completamento non viene mai chiamato.Ad esempio:

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

Posso vedere l'uscita della console nella mia timeline del parco giochi, ma il println nel mio blocco di completamento non viene mai chiamato ...

È stato utile?

Soluzione

Mentre è possibile eseguire manualmente un ciclo di esecuzione (o, per il codice asincrono che non richiede un ciclo di esecuzione, utilizzare altri metodi di attesa come semafori della spedizione), il modo "integrato" che forniamo nei parchi giochi per aspettare per aspettareIl lavoro è di importare il framework XCPlayground e impostare XCPlaygroundPage.currentPage.needsIndefiniteExecution = true.Se questa proprietà è stata impostata, quando la tua sorgente del parco giochi di primo piano finisce, invece di fermare il parco giochi lì continueremo a girare il ciclo principale di run, il codice così asincrono ha la possibilità di correre.Alla fine termineremo il parco giochi dopo un timeout che è predefinito a 30 secondi, ma che può essere configurato se si apre l'Assistant Editor e mostra l'Assistente Timeline;Il timeout è in basso a destra.

Ad esempio, in Swift 3 (utilizzando URLSession anziché 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
.

o in 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
.

Altri suggerimenti

Questo API è cambiato di nuovo in XCode 8 ed è stato spostato al PlaygroundSupport:

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true
.

Questo cambiamento è stato menzionato in Sessione 213 su WWDC 2016 .

AS di XCode 7.1, XCPSetExecutionShouldContinueIndefinitely() è deprecato.Il modo corretto per farlo ora è quello di richiedere prima l'esecuzione indefinita come proprietà della pagina corrente:

import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
.

... quindi indicare quando l'esecuzione ha finito con:

XCPlaygroundPage.currentPage.finishExecution()
.

Ad esempio:

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

Il motivo per cui i callback non vengono chiamati è perché il runloop non è in esecuzione in Playground (o in modalità Rept per quella materia).

A un po 'janky, ma efficace, il modo per far funzionare i callback è con una bandiera e quindi itering manualmente sul 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)
}
.

Questo motivo è stato spesso utilizzato nei test dell'unità che devono testare i callback di Async, ad esempio: Pattern per il test dell'unità Async Queue che chiama la coda principale sul completamento

Le nuove API come per Xcode 8, Swift 3 e IOS 10 sono,

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

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

Swift 3, Xcode 8, iOS 10

Note:

Dì al compilatore che il file del parco giochi richiede "Esecuzione indefinita"

Termina manualmente l'esecuzione tramite una chiamata a PlaygroundSupport.current.completeExecution() all'interno del gestore di completamento.

È possibile che si tratti di problemi con la directory della cache e di risolvere questo è necessario re-istanziare manualmente l'uiceche.Shared Singleton.

Esempio:

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()
.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top