Как реализовать NSRunLoop внутри NSOperation

Я отправляю этот вопрос, потому что я видел много путаницы по этой теме, и в результате я потратил несколько часов на отладку подклассов NSOperation.

Проблема в том, что NSOperation не приносит вам большой пользы, когда вы выполняете асинхронные методы, которые не являютсяactually complete пока асинхронный обратный вызов не завершится.

Если сама NSOperation является делегатом обратного вызова, этого может быть даже недостаточно для правильного завершения операции из-за обратного вызова, происходящего в другом потоке.

Допустим, вы находитесь в главном потоке, и вы создаете NSOperation и добавляете его в NSOperationQueue. Код внутри NSOperation запускает асинхронный вызов, который вызывает некоторый метод в AppDelegate или контроллере представления.

Вы не можете заблокировать основной поток, или пользовательский интерфейс заблокируется, поэтому у вас есть два варианта.

1) Create an NSOperation and add it to the NSOperationQueue with the following signature:

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

Удачи с этим. Асинхронные операции обычно требуют цикла выполнения, поэтому он не будет работать, если вы не создадите подкласс NSOperation или не используете блок, но даже блок не будет работать, если вам нужно "завершить" NSOperation, сообщая ему, когда обратный вызов завершен.

Итак ... вы подкласс NSOperation с чем-то похожим на следующее, чтобы обратный вызов мог сообщить операции, когда она завершена:

//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 - This absolutely does not work - we got that out of the way!

2) Вторая проблема с этим методом состоит в том, что, допустим, вышеприведенное действительно работает (а это не так) в случаях, когда runLoops должен завершаться должным образом (или фактически завершаться вообще из вызова внешнего метода в обратном вызове).

Предположим, на секунду я нахожусь в главном потоке, когда я вызываю это, если только я не хочу, чтобы пользовательский интерфейс немного заблокировался и ничего не рисовал, я не могу сказать & quot; waitUntilFinished: YES & quot; на метод addOperation NSOperationQueue ...

So how do I accomplish the same behavior as waitUntilFinished:YES without locking up the main thread?

Так как в Какао так много вопросов, касающихся runLoops, NSOperationQueues и Asynch, я опубликую свое решение как ответ на этот вопрос.

Обратите внимание, что я отвечаю только на свой собственный вопрос, потому что я проверил meta.stackoverflow, и они сказали, что это приемлемо и поощряется, я надеюсьthe answer that follows помогает людям понять, почему их циклы выполнения блокируются в NSOperations и как они могут правильно выполнять NSOperations из внешних обратных вызовов. (Обратные вызовы в других темах)

Ответы на вопрос(3)

Ваш ответ на вопрос