Я публикую этот вопрос, потому что я видел много путаницы по этой теме, и я провел несколько часов отладки подклассов NSOperation в результате.
Проблема заключается в том, что NSOperation не очень хорошо работает при выполнении асинхронных методов, которые на самом деле не завершены до завершения асинхронного обратного вызова.
Если сам NSOperation является делегатом callback, он может даже оказаться недостаточным для правильного завершения операции из-за обратного вызова, происходящего в другом потоке.
Предположим, что вы находитесь в основном потоке, и вы создаете NSOperation и добавляете его в NSOperationQueue, код внутри NSOperation запускает асинхронный вызов, который обращается к некоторому методу в AppDelegate или контроллере представления.
Вы не можете заблокировать основной поток или пользовательский интерфейс заблокируется, поэтому у вас есть два варианта.
1) Создайте NSOperation и добавьте его в NSOperationQueue со следующей подписью:
[NSOperationQueue addOperations: @[myOp] waitUntilFinished:?]
Удачи вам в этом. Асинхронным операциям обычно требуется runloop, поэтому он не будет работать, если вы не подклассируете 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;
}
ОК - это абсолютно не сработало - у нас это не сработало!
2) Вторая проблема с этим методом заключается в том, что позволяет сказать, что выполняемое на самом деле работало (а это не так) в случаях, когда runLoops должны заканчиваться должным образом (или фактически вообще заканчиваются с внешнего вызов метода в обратном вызове)
Давайте предположим для второго Im в основном потоке, когда я это вызвал, если я не хочу, чтобы пользовательский интерфейс блокировался немного, и ничего не нарисовал, я не могу сказать "waitUntilFinished: YES" в методе addOperation NSOperationQueue..
Итак, как мне выполнить то же поведение, что и waitUntilFinished: YES, не блокируя основной поток?
Так как есть много вопросов относительно runLoops, NSOperationQueues и поведения Asynch в Cocoa, я отправлю свое решение в качестве ответа на этот вопрос.
Обратите внимание, что Im только отвечает на мой собственный вопрос, потому что я проверил meta.stackoverflow, и они сказали, что это приемлемо и приветствуется. Я надеюсь, что ответ, который следует за, помогает людям понять, почему их runloops блокируются в NSOperations и то, как они могут правильно выполнять NSOperations из внешних обратных вызовов. (Обратные вызовы для других потоков)