2010-03-24 9 views
5

Ho un ciclo while, che viene eseguito per molti secondi ed è per questo che voglio aggiornare una barra di avanzamento (NSProgressIndicator) durante questo processo, ma si aggiorna solo una volta terminato il ciclo. Lo stesso accade se voglio aggiornare un testo etichetta, tra l'altro.Come aggiorno una barra di avanzamento in Cocoa durante un lungo ciclo di esecuzione?

Credo che il mio ciclo impedisca il verificarsi di altre cose di quell'applicazione. Ci deve essere un'altra tecnica. Questo ha a che fare con discussioni o qualcosa del genere? Sono sulla buona strada? Qualcuno può per favore darmi un semplice esempio, come "ottimizzare" la mia applicazione?

La mia domanda è un'applicazione Cocoa (Xcode 3.2.1) con questi due metodi nel mio 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]; 
} 

Sono davvero una novità per Objective-C, cacao e le applicazioni con un'interfaccia utente. Grazie mille per qualsiasi risposta utile.

risposta

15

Se si sta costruendo per Snow Leopard, la soluzione più semplice è a mio parere da usare blocchi e Grand Central Dispatch.

Il seguente codice mostra come apparirà il vostro metodo startIt: quando si utilizza GCD.

Il tuo metodo stopIt: dovrebbe funzionare come hai scritto. Il motivo per cui non funzionava prima è che gli eventi del mouse si verificano sul thread principale e quindi il pulsante non ti ha risposto perché stavi lavorando sul thread principale. Questo problema avrebbe dovuto essere risolto ora poiché il lavoro è stato messo su un thread diverso ora con GCD. Prova il codice, e se non funziona, fammi sapere e vedrò se ho fatto qualche errore in esso.

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

Grazie mille, darò è una prova questo fine settimana! – Nick

+0

Questo è quello che stavo cercando.Funziona bene e questo mi dà un buon punto di partenza per saperne di più. Grazie! – Nick

+0

Grazie per questa Enchilada! Ho appena imparato qualcosa che non sapevo! Molto eccitato per scoprire blocchi e GCD !! –

2

Credo che il mio ciclo impedisca il verificarsi di altre cose di quell'applicazione.

Corretto. Hai bisogno di rompere questo in qualche modo.

Un modo sarebbe un timer, con te che tagli la coda un po 'alla volta nella richiamata del timer. Un altro sarebbe quello di avvolgere il codice per gestire un elemento in una sottoclasse NSOperation e creare istanze di tale classe (operazioni) e inserirle in un NSOperationQueue.

Questo ha a che fare con fili o qualcosa del genere?

Non necessariamente. NSOperations viene eseguito sui thread, ma NSOperationQueue gestirà lo spawn del thread per te. Un timer è una soluzione a thread singolo: ogni timer viene eseguito sul thread su cui lo si pianifica. Questo può essere un vantaggio o uno svantaggio, decidi tu.

Vedere the threads section of my intro to Cocoa per ulteriori dettagli.

3

Si può provare questo codice ..

[progressbar setUsesThreadedAnimation:YES]; 
2

questo ha funzionato per me, che è una combinazione di risposte da altri che non sembra funzionare (almeno per me) per conto proprio:

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 
}); 
Problemi correlati