2010-03-24 9 views
5

Ich habe eine while Schleife, die für viele Sekunden ausgeführt wird, und deshalb möchte ich eine Fortschrittsanzeige (NSProgressIndicator) während dieses Prozesses aktualisieren, aber es aktualisiert nur einmal nach der Schleife abgeschlossen ist. Das Gleiche passiert übrigens, wenn ich einen Etikettentext aktualisieren möchte.Wie aktualisiere ich eine Fortschrittsanzeige in Cocoa während einer langen laufenden Schleife?

Ich glaube, meine Schleife verhindert, dass andere Dinge dieser Anwendung passieren. Es muss eine andere Technik geben. Hat das mit Threads oder sowas zu tun? Bin ich auf dem richtigen Weg? Kann mir bitte jemand ein einfaches Beispiel geben, wie ich meine Bewerbung "optimieren" kann?

Meine Anwendung ist eine Cocoa-Anwendung (Xcode 3.2.1) mit diesen beiden Methoden in meinem Example_AppDelegate.m:

 
// This method runs when a start button is clicked. 
- (IBAction)startIt:(id)sender { 
    [progressbar setDoubleValue:0.0]; 
    [progressbar startAnimation:sender]; 
    running = YES; // this is a instance variable 

    int i = 0; 
    while (running) { 
     if (i++ >= processAmount) { // processAmount is something like 1000000 
      running = NO; 
      continue; 
     } 

     // Update progress bar 
     double progr = (double)i/(double)processAmount; 
     NSLog(@"progr: %f", progr); // Logs values between 0.0 and 1.0 
     [progressbar setDoubleValue:progr]; 
     [progressbar needsDisplay]; // Do I need this? 

     // Do some more hard work here... 
    } 
} 

// This method runs when a stop button is clicked, but as long 
// as -startIt is busy, a click on the stop button does nothing. 
- (IBAction)stopIt:(id)sender { 
    NSLog(@"Stop it!"); 
    running = NO; 
    [progressbar stopAnimation:sender]; 
} 

I Objective-C, Cocoa und Anwendungen mit einer Benutzeroberfläche, um wirklich neu bin. Vielen Dank für eine hilfreiche Antwort.

Antwort

15

Wenn Sie Snow Leopard bauen, ist die einfachste Lösung meiner Meinung nach Blöcke und Grand Central Dispatch zu verwenden.

Der folgende Code zeigt Ihnen, wie Ihre Methode startIt: bei Verwendung von GCD aussehen würde.

Ihre stopIt: Methode sollte gut funktionieren, wie Sie es geschrieben haben. Der Grund, warum es vorher nicht funktionierte, ist, dass Mausereignisse im Hauptthread passieren und somit der Button nicht auf dich reagierte, weil du an dem Hauptthread gearbeitet hast. Dieses Problem sollte jetzt behoben sein, da die Arbeit jetzt mit GCD auf einen anderen Thread gesetzt wurde. Probieren Sie den Code aus, und wenn es nicht funktioniert, lassen Sie es mich wissen und ich werde sehen, ob ich Fehler gemacht habe.

// This method runs when a start button is clicked. 
- (IBAction)startIt:(id)sender { 

    //Create the block that we wish to run on a different thread. 
    void (^progressBlock)(void); 
    progressBlock = ^{ 

    [progressbar setDoubleValue:0.0]; 
    [progressbar startAnimation:sender]; 
    running = YES; // this is a instance variable 

    int i = 0; 
    while (running) { 
     if (i++ >= processAmount) { // processAmount is something like 1000000 
      running = NO; 
      continue; 
     } 

     // Update progress bar 
     double progr = (double)i/(double)processAmount; 
     NSLog(@"progr: %f", progr); // Logs values between 0.0 and 1.0 

     //NOTE: It is important to let all UI updates occur on the main thread, 
     //so we put the following UI updates on the main queue. 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [progressbar setDoubleValue:progr]; 
      [progressbar setNeedsDisplay:YES]; 
     }); 

     // Do some more hard work here... 
    } 

    }; //end of progressBlock 

    //Finally, run the block on a different thread. 
    dispatch_queue_t queue = dispatch_get_global_queue(0,0); 
    dispatch_async(queue,progressBlock); 
} 
+0

Vielen Dank, ich geb es ein Versuch dieses Wochenende! – Nick

+0

Das ist, was ich gesucht habe.Funktioniert gut und das gibt mir einen guten Ausgangspunkt, um mehr darüber zu erfahren. Vielen Dank! – Nick

+0

Danke für diesen Enchilada! Ich habe gerade etwas gelernt, was ich nicht wusste! Sehr aufgeregt, um über Blöcke und GCD zu erfahren !! –

2

Ich glaube, meine Schleife verhindert, dass andere Dinge dieser Anwendung passieren.

Korrekt. Du musst das irgendwie auflösen.

Ein Weg wäre ein Timer, mit dem Sie die Warteschlange im Timer-Callback nach und nach verkleinern. Ein anderer wäre, den Code so zu verpacken, dass er ein Element in einer NSOperation-Unterklasse behandelt, und Instanzen dieser Klasse (Operationen) zu erstellen und sie in eine NSOperationQueue zu schreiben.

Hat dies mit Threads oder etwas zu tun?

Nicht unbedingt. NSOperations werden in Threads ausgeführt, aber die NSOperationQueue verarbeitet den Thread für Sie. Ein Timer ist eine Singlethread-Lösung: Jeder Timer wird auf dem Thread ausgeführt, auf dem Sie ihn planen. Das kann ein Vorteil oder ein Nachteil sein - Sie entscheiden.

Weitere Informationen finden Sie unter the threads section of my intro to Cocoa.

3

Sie diesen Code wieder ..

[progressbar setUsesThreadedAnimation:YES]; 
2

Dieser arbeitete für mich, das ist eine Kombination von Antworten von den anderen ist, die nicht zu arbeiten schien (zumindest für mich) auf ihrem eigenen:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 
    //do something 
    dispatch_async(dispatch_get_main_queue(), ^{ 
    progressBar.progress = (double)x/(double)[stockList count];    
    }); 
    //do something else 
}); 
Verwandte Themen