2010-10-07 10 views
12

Ich versuche, eine Methode aus einer Klasse zu verwenden, die ich irgendwo heruntergeladen habe. Die Methode wird im Hintergrund ausgeführt, während die Programmausführung fortgesetzt wird. Ich möchte nicht zulassen, dass die Programmausführung fortgesetzt wird, bis diese Methode beendet ist. Wie mache ich das?Warten auf das Ende eines Threads in Objective-C

Antwort

0

Ich würde vorschlagen, Aufruf der Klassenmethode in Ihrer eigenen Methode, und legen Sie einen booleschen, sobald es abgeschlossen ist. Für zB:

BOOL isThreadRunning = NO; 
- (void)beginThread { 
    isThreadRunning = YES; 

    [self performSelectorInBackground:@selector(backgroundThread) withObject:nil]; 
} 
- (void)backgroundThread { 
    [myClass doLongTask]; 

    // Done! 
    isThreadRunning = NO; 
} 
- (void)waitForThread { 
    if (! isThreadRunning) { 
     // Thread completed 
     [self doSomething]; 
    } 
} 

Wie Sie warten behandeln wollen, bleibt Ihnen überlassen: Vielleicht Polling mit [NSThread sleepForTimeInterval: 1] oder ähnliches, oder selbst eine Nachricht jeder Laufschleife sendet.

+0

Ich tat dies, und es funktioniert, aber es verhindert auch, dass das HUD angezeigt wird. –

+0

Sie könnten NSTimer verwenden, um Benutzeroberflächenupdates abzufragen und zu behalten, die korrekt funktionieren. Alles hängt davon ab, was Sie brauchen. Für eine einfache Aufgabe und dann aktualisieren Dereks Antwort wäre sauberer und weniger lästig. – v01d

+0

Für eine Spielengine oder etwas mehr Echtzeit kann ein Timer ohnehin schon vorhanden sein. Wenn du dann einen Thread losschickst und vergisst, ist es vielleicht mehr, was du brauchst. – v01d

6

Meine erste Neigung ist es nicht zu tun, was Sie vorschlagen. Die Technik, die ich zuvor verwendet habe, ist, dem Thread einen Selektor für eine Methode im Ursprungsobjekt (die sich im Hauptthread befindet) zu geben. Wenn der zweite Thread gestartet wird, wird der Hauptthread weiterhin ausgeführt, aber auf dem Display wird eine Besetztzeichenanzeige angezeigt. Dadurch kann die Benutzerinteraktion bei Bedarf fortgesetzt werden.

Wenn der zweite Thread beendet wird, bevor er beendet wird, ruft er den Selektor auf dem Hauptthread auf. Die Methode, auf die der Selektor verweist, entfernt dann den Busy-Indikator von der Anzeige und teilt dem Haupt-Thread mit, was er aktualisiert hat. Er nimmt alle Daten auf, die der zweite Thread erzeugt hat.

Ich habe dies erfolgreich für eine App verwendet, die auf einen Webdienst zugreift (im zweiten Thread) und dann die Anzeige aktualisiert, sobald Daten zurückgegeben werden, ohne sie zu sperren. Dies macht die Benutzererfahrung viel schöner.

+1

Dies ist ein schönes System für UI-Updates; Sie können ein Schmerz sein. – v01d

8

Verwenden Sie ein NSOperationQueue, wie diese
(aus dem Gedächtnis vergeben so keine kleinere Fehler - Sie werden die Grundidee erhalten):


// ivars 
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init]; 
// count can be anything you like 
[opQueue setMaxConcurrentOperationCount:5]; 

- (void)main 
{ 
    [self doStuffInOperations]; 
} 

// method 
- (void)doStuffInOperations 
{ 
    // do parallel task A 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"a"] autorelease]]; 

    // do parallel task B 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"b"] autorelease]]; 

    // do parallel task C 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"c"] autorelease]]; 


    [opQueue waitUntilAllOperationsHaveFinished]; 

    // now, do stuff that requires A, B, and C to be finished, and they should be finished much faster because they are in parallel. 
} 

- (void)doSomething:(id)arg 
{ 
    // do whatever you want with the arg here 
    // (which is in the background, 
    // because all NSOperations added to NSOperationQueues are.) 
} 


+1

Ich weiß, dass ich diese Frage nicht gestellt habe, aber das war, was ich mit meiner App gesucht habe, da ich Race Conditions hatte und ich wusste, dass sie schneller waren, als der Code konnte . Dies löste mein großes Problem. Also danke, mein Herr, ich denke, das sollte als die richtige Antwort markiert werden, weil es keine Arbeit verwendet und eine Thread-sichere Kodierung mit der Ios-Bibliothek beinhaltet. – Rob

+0

Zweitens, was Rob sagte, das ist genau das, was ich gesucht habe! – AdamM

+1

@AdamM - überprüfen Sie die andere Antwort, die ich heute gepostet habe. Es ist ein viel sauberer Weg, es zu tun und GCD wird für alle iOS 4 und höher unterstützt. Ein bemerkenswerter Vorteil dieser Methode besteht darin, dass Sie keine Abstürze mehr bekommen, wenn Objekte von Ihnen entfernt werden, da GCD alle Objekte, auf die Sie verweisen, automatisch speichert und freigibt. – jpswain

35

Hier ist eine andere Art und Weise, es zu tun GCD mit:



- (void)main 
{ 
    [self doStuffInOperations]; 
} 

- (void)doStuffInGCD 
{ 
    dispatch_group_t d_group = dispatch_group_create(); 
    dispatch_queue_t bg_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"a"]; 
    }); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"b"]; 
    }); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"c"]; 
    }); 


    // you can do this to synchronously wait on the current thread: 
    dispatch_group_wait(d_group, DISPATCH_TIME_FOREVER); 
    dispatch_release(d_group); 
    NSLog(@"All background tasks are done!!"); 


    // **** OR **** 

    // this if you just want something to happen after those are all done: 
    dispatch_group_notify(d_group, dispatch_get_main_queue(), ^{ 
     dispatch_release(d_group); 
     NSLog(@"All background tasks are done!!");   
    }); 
} 

- (void)doSomething:(id)arg 
{ 
    // do whatever you want with the arg here 
} 
+4

Prost! Diese Methode ist noch besser – AdamM

+0

+1 für die richtige Antwort. – Abizern

+0

@node ninja Würde es Ihnen etwas ausmachen, eine Antwort auf Ihre Frage auszuwählen? – jpswain

0

Meine Technik ist zu prüfen, ob die Aufgabe (wenn der Hintergrund-Thread beendet ist) ausgeführt werden kann, und wenn es nicht dann ich GCD laufen kann wieder verwenden, um zu versuchen nach einer Verzögerung:

- (void)doSomething 
{ 
    if (/* is the other thread done yet? */) { 
    double delayInSeconds = 2.0; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
     [self doSomething]; 
    }); 
    return; 
    } 

    // do something 
} 
1

Mehrere technische Möglichkeiten, Multi-Threads zu synchronisieren, wie NSConditionLock (Mutex-Sperre), NSCondition (Semaphor). Aber sie sind gemeinsame Programmierkenntnisse für andere Sprachen (Java ...) neben Objective-c. Ich ziehe es run loop (Sonder in Cocoa) einzuführen Thread zu implementieren verbinden:

NSThread *A; //global 
A = [[NSThread alloc] initWithTarget:self selector:@selector(runA) object:nil]; //create thread A 
[A start]; 

- (void)runA  
{ 
  [NSThread detachNewThreadSelector:@selector(runB) toTarget:self withObject:nil]; //create thread B  
  while (1)  
  {  
    if ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) //join here, waiting for thread B  
    {  
      NSLog(@"thread B quit...");  
      break;  
    }  
  }  
} 

- (void)runB  
{  
  sleep(1);  
  [self performSelector:@selector(setData) onThread:A withObject:nil waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];  
} 
3

Für solche Fälle ich in der Regel NSCondition Klasse.

//this method executes in main thread/queue 
- (void)waitForJob 
{ 
    id __weak selfWeak = self; 
    NSCondition *waitHandle = [NSCondition new]; 
    [waitHandle lock]; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
     [selfWeak doSomethingLongtime]; 
     [waitHandle signal]; 
    }); 
    //waiting for background thread finished 
    [waitHandle waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:60]]; 
} 
Verwandte Themen