Implementieren einer NSRunLoop in einer NSOperation

Ich poste diese Frage, weil ich viel Verwirrung in Bezug auf dieses Thema gesehen habe und deshalb mehrere Stunden damit verbracht habe, NSOperation-Unterklassen zu debuggen.

Das Problem ist, dass NSOperation nicht viel bringt, wenn Sie asynchrone Methoden ausführen, die es nicht sindeigentlich komplett bis der asynchrone Rückruf abgeschlossen ist.

Wenn die NSOperation selbst der Rückrufdelegierte ist, reicht dies möglicherweise nicht aus, um den Vorgang ordnungsgemäß abzuschließen, da der Rückruf in einem anderen Thread erfolgt.

Nehmen wir an, Sie befinden sich im Hauptthread und erstellen eine NSOperation und fügen sie einer NSOperation hinzu. QDer Code in der NSOperation löst einen asynchronen Aufruf aus, der eine Methode auf dem AppDelegate oder einem View-Controller zurückruft.

Sie können den Haupt-Thread nicht blockieren, da die Benutzeroberfläche sonst blockiert. Sie haben also zwei Möglichkeiten.

1) Erstellen Sie eine NSOperation und fügen Sie sie mit der folgenden Signatur zur NSOperationQueue hinzu:

[NSOperationQueue addOperations: @ [myOp] waitUntilFinished :?]

Viel Glück damit. Asynchrone Vorgänge erfordern normalerweise einen Runloop, sodass sie nur funktionieren, wenn Sie NSOperation als Unterklasse definieren oder einen Block verwenden. Sogar ein Block funktioniert nicht, wenn Sie die NSOperation "abschließen" müssen, indem Sie ihm mitteilen, wann der Rückruf abgeschlossen ist.

Sie unterteilen NSOperation also in eine Unterklasse, die ungefähr der folgenden entspricht, damit der Rückruf den Vorgang erkennen kann, wenn er abgeschlossen ist:

//you create an NSOperation subclass it includes a main method that
//keeps the runloop going as follows
//your NSOperation subclass has a BOOL field called "complete"

-(void) main
{

    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

    //I do some stuff which has async callbacks to the appDelegate or any other class (very common)

    while (!complete && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

}

//I also have a setter that the callback method can call on this operation to 
//tell the operation that its done, 
//so it completes, ends the runLoop and ends the operation

-(void) setComplete {
    complete = true;
}

//I override isFinished so that observers can see when Im done
// - since my "complete" field is local to my instance

-(BOOL) isFinished
{
    return complete;
}

OK - Das funktioniert absolut nicht - das haben wir aus dem Weg geräumt!

2) Das zweite Problem bei dieser Methode ist, dass das oben Gesagte tatsächlich funktioniert hat (was nicht der Fall ist), wenn runLoops ordnungsgemäß beendet werden muss (oder überhaupt von einem externen Methodenaufruf in einem Rückruf beendet werden muss).

Nehmen wir für eine Sekunde an, dass ich im Hauptthread bin, wenn ich das aufrufe, es sei denn, ich möchte, dass sich die Benutzeroberfläche ein wenig sperrt und nichts zeichnet.

Wie bewerkstellige ich das gleiche Verhalten wie waitUntilFinished: YES, ohne den Haupt-Thread zu sperren?

Da es in Cocoa so viele Fragen zum Verhalten von runLoops, NSOperationQueues und Asynch gibt, werde ich meine Lösung als Antwort auf diese Frage veröffentlichen.

Beachten Sie, dass ich nur meine eigene Frage beantworte, weil ich meta.stackoverflow überprüft habe und sie sagten, dass dies akzeptabel und ermutigt ist, hoffe ichdie Antwort folgt hilft den Leuten zu verstehen, warum ihre Runloops in NSOperations blockieren und wie sie NSOperations von externen Rückrufen ordnungsgemäß abschließen können. (Rückrufe auf andere Threads)

Antworten auf die Frage(3)

Ihre Antwort auf die Frage