2014-11-04 14 views
9

In meinem Code habe ich eine einfache for-Schleife, die 100-mal mit verschachtelten for-Schleifen eine Schleife erstellt, um eine Verzögerung zu erstellen. Nach der Verzögerung aktualisiere ich ein Fortschrittsanzeigeelement in der Benutzeroberfläche mit einem Dispatch_async. Ich kann die Benutzeroberfläche jedoch nicht aktualisieren. Weiß jemand, warum die Benutzeroberfläche nicht aktualisiert wird? Hinweis: Mit der folgenden print-Anweisung wird überprüft, ob die for-Schleife korrekt durchlaufen wurde.Aktualisieren der Benutzeroberfläche mit Dispatch_Async in Swift

for i in 0..<100 { 

    //Used to create a delay 
    for var x = 0; x<100000; x++ { 
     for var z = 0; z<1000; z++ { 

     } 
    } 

    println(i) 

    dispatch_async(dispatch_get_main_queue()) { 
     // update some UI 
     self.progressView.setProgress(Float(i), animated: true) 

    } 
    } 
+0

Können Sie bestätigen, dass die setProgress Anruf feuert? Versuchen Sie, sich dort einzuloggen oder einen Logging Breakpoint zu verwenden. Außerdem ist es im Allgemeinen vorzuziehen, NSOperation für Dinge zu verwenden, die Kakao verwenden. – macshome

+0

Interessant, ich warf einige Logging und es scheint, dass der Aufruf setProgress nicht feuert. Warum sollte das sein? – dslkfjsdlfjl

+0

Nun, wenn Sie dispatch_async, ist es an das System, als ob es feuern wird. Wenn Sie die Benutzeroberfläche rechtzeitig aktualisieren müssen, verwenden Sie keine Asynchronisierung. In Xcode können Sie die App pausieren und sich die ausstehenden Blöcke ansehen. – macshome

Antwort

33

Drei Beobachtungen, zwei grundlegende, eine ein wenig weiter fortgeschritten:

  1. Ihre Schleife wird die Benutzeroberfläche in diesem Haupt-Thread zu aktualisieren, es sei denn, die Schleife selbst läuft auf einem anderen Thread nicht in der Lage sein. Sie können es also an eine Hintergrundwarteschlange senden. In Swift 3:

    DispatchQueue.global(qos: .utility).async { 
        for i in 0 ..< kNumberOfIterations { 
    
         // do something time consuming here 
    
         DispatchQueue.main.async { 
          // now update UI on main thread 
          self.progressView.setProgress(Float(i)/Float(kNumberOfIterations), animated: true) 
         } 
        } 
    } 
    

    In Swift 2:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
        for i in 0 ..< kNumberOfIterations { 
    
         // do something time consuming here 
    
         dispatch_async(dispatch_get_main_queue()) { 
          // now update UI on main thread 
          self.progressView.setProgress(Float(i)/Float(kNumberOfIterations), animated: true) 
         } 
        } 
    } 
    
  2. Beachten Sie auch, dass der Fortschritt eine Zahl von 0,0 bis 1,0 ist, so dass Sie vermutlich durch die maximale Anzahl von Wiederholungen für die geteilt werden sollen Schleife.

  3. Wenn UI-Aktualisierungen schneller aus dem Hintergrundthread kommen, als die UI sie verarbeiten kann, kann der Hauptthread mit Aktualisierungsanforderungen zurückgestoßen werden (wodurch er viel langsamer aussieht als er wirklich ist). Um dies zu beheben, könnte man die Verteilungsquelle verwenden, um die Aufgabe "update UI" von dem tatsächlichen Hintergrundaktualisierungsprozess zu entkoppeln.

    kann man verwenden, um ein DispatchSourceUserDataAdd (in Swift 2, es ist ein dispatch_source_t von DISPATCH_SOURCE_TYPE_DATA_ADD ist), post add Anrufe (dispatch_source_merge_data in Swift 2) aus dem Hintergrund-Thread so oft wie gewünscht, und die UI wird sie so schnell verarbeiten, wie es kann , aber wird sie zusammen zusammenführen, wenn es data (dispatch_source_get_data in Swift 2) aufruft, wenn die Hintergrundaktualisierungen schneller kommen, als die UI sie andernfalls verarbeiten kann. Dadurch wird eine maximale Hintergrundleistung mit optimierten UI-Aktualisierungen erreicht. Vor allem aber wird sichergestellt, dass die Benutzeroberfläche nicht zu einem Engpass wird. So

    erklären zunächst einige variable Spur des Fortschritts zu halten:

    var progressCounter: UInt = 0 
    

    Und jetzt Ihre Schleife eine Quelle erstellen können, definieren, was zu tun ist, wenn die Quelle aktualisiert wird, und dann die asynchrone Schleife starten die aktualisiert die Quelle. In Swift 3, das ist:

    progressCounter = 0 
    
    // create dispatch source that will handle events on main queue 
    
    let source = DispatchSource.makeUserDataAddSource(queue: .main) 
    
    // tell it what to do when source events take place 
    
    source.setEventHandler() { [unowned self] in 
        self.progressCounter += source.data 
    
        self.progressView.setProgress(Float(self.progressCounter)/Float(kNumberOfIterations), animated: true) 
    } 
    
    // start the source 
    
    source.resume() 
    
    // now start loop in the background 
    
    DispatchQueue.global(qos: .utility).async { 
        for i in 0 ..< kNumberOfIterations { 
         // do something time consuming here 
    
         // now update the dispatch source 
    
         source.add(data: 1) 
        } 
    } 
    

    In Swift 2:

    progressCounter = 0 
    
    // create dispatch source that will handle events on main queue 
    
    let source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue()); 
    
    // tell it what to do when source events take place 
    
    dispatch_source_set_event_handler(source) { [unowned self] in 
        self.progressCounter += dispatch_source_get_data(source) 
    
        self.progressView.setProgress(Float(self.progressCounter)/Float(kNumberOfIterations), animated: true) 
    } 
    
    // start the source 
    
    dispatch_resume(source) 
    
    // now start loop in the background 
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
        for i in 0 ..< kNumberOfIterations { 
    
         // do something time consuming here 
    
         // now update the dispatch source 
    
         dispatch_source_merge_data(source, 1); 
        } 
    } 
    
+0

Vielen Dank!Sehr informativ und es macht genau das, was ich tun musste! – dslkfjsdlfjl

Verwandte Themen