Jak zaimplementować NSRunLoop wewnątrz NSOperation

Umieszczam to pytanie, ponieważ widziałem wiele zamieszania w tym temacie i przez kilka godzin debugowałem podklasy NSOperation.

Problem polega na tym, że NSOperation nie robi wiele dobrego, gdy wykonujesz metody asynchroniczne, które nie sąfaktycznie zakończone aż do zakończenia asynchronicznego wywołania zwrotnego.

Jeśli sama NSOperation jest delegatem wywołania zwrotnego, może nawet nie być wystarczające, aby poprawnie zakończyć operację z powodu wywołania zwrotnego występującego w innym wątku.

Powiedzmy, że jesteś w głównym wątku i tworzysz NSOperation i dodajesz go do NSOperationQueue, kod wewnątrz NSOperation wywołuje asynchroniczne wywołanie, które oddzwania do jakiejś metody w AppDelegate lub kontrolerze widoku.

Nie możesz zablokować głównego wątku lub interfejs użytkownika zostanie zablokowany, więc masz dwie opcje.

1) Utwórz NSOperation i dodaj go do NSOperationQueue z następującym podpisem:

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

Powodzenia z tym. Operacje asynchroniczne zwykle wymagają runloopa, więc nie będzie działać, chyba że podklasujesz NSOperation lub użyjesz bloku, ale nawet blok nie zadziała, jeśli musisz „ukończyć” NSOperation, informując go o zakończeniu wywołania zwrotnego.

Więc ... podklasujesz NSOperation za pomocą czegoś podobnego do poniższego, więc wywołanie zwrotne może powiedzieć operację po jej zakończeniu:

//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 - to absolutnie nie działa - mamy to na uboczu!

2) Drugi problem z tą metodą polega na tym, że powiedzmy, że powyższe faktycznie działało (czego nie robi) w przypadkach, gdy runLoops muszą zostać poprawnie zakończone (lub w rzeczywistości kończą się z zewnętrznego wywołania metody w wywołaniu zwrotnym)

Załóżmy na sekundę Im w głównym wątku, kiedy to nazywam, chyba że chcę, aby interfejs użytkownika zablokował się na jakiś czas i nie malował niczego, nie mogę powiedzieć „waitUntilFinished: YES” w metodzie addOperation NSOperationQueue ...

Jak więc osiągnąć to samo zachowanie co waitUntilFinished: TAK bez blokowania głównego wątku?

Ponieważ jest wiele pytań dotyczących zachowania runLoops, NSOperationQueues i Asynch w Cocoa, opublikuję moje rozwiązanie jako odpowiedź na to pytanie.

Zauważ, że odpowiadam tylko na własne pytanie, ponieważ sprawdziłem meta.stackoverflow i powiedzieli, że jest to akceptowalne i zachęcane, mam nadziejęodpowiedź, która następuje pomaga ludziom zrozumieć, dlaczego ich runloopsy zamykają się w NSOperations i jak mogą poprawnie wykonywać NSOperacje z zewnętrznych wywołań zwrotnych. (Callbacki na innych wątkach)

questionAnswers(3)

yourAnswerToTheQuestion