Хорошо, насколько я понимаю, у вас есть два вопроса:
Вам нужен сегмент performSelectorOnMainThread:
, который появляется в комментариях в вашем коде? Что делает этот код?
Почему флаг _isCancelled
не изменяется при вызове cancelAllOperations
на NSOperationQueue
, который содержит эту операцию?
Давайте разберемся с этим по порядку. Я собираюсь предположить, что ваш подкласс NSOperation
называется MyOperation
, просто для простоты объяснения. Я объясню, что вы неправильно понимаете, а затем приведу исправленный пример.
1. Одновременный запуск NSOperations
В большинстве случаев вы будете использовать NSOperation
s с NSOperationQueue
, и из вашего кода это звучит так, как будто вы это делаете. В этом случае ваш MyOperation
всегда будет выполняться в фоновом потоке, независимо от того, что возвращает метод -(BOOL)isConcurrent
, поскольку NSOperationQueue
явно предназначены для выполнения операций в фоновом режиме.
Таким образом, вам, как правило, нет необходимости переопределять метод -[NSOperation start]
, поскольку по умолчанию он просто вызывает метод -main
. Это метод, который вы должны переопределить. Метод -start
по умолчанию уже обрабатывает для вас настройки isExecuting
и isFinished
в соответствующее время.
Поэтому, если вы хотите, чтобы NSOperation
работал в фоновом режиме, просто переопределите метод -main
и поместите его в NSOperationQueue
.
performSelectorOnMainThread:
в вашем коде заставит каждый экземпляр MyOperation
всегда выполнять свою задачу в главном потоке. Поскольку в потоке одновременно может выполняться только один фрагмент кода, это означает, что другие MyOperation
не могут быть запущены. Вся цель NSOperation
и NSOperationQueue
- сделать что-то в фоновом режиме.
Единственный раз, когда вы хотите навязать что-либо в основной поток, это когда вы обновляете пользовательский интерфейс. Если вам нужно обновить пользовательский интерфейс после того, как ваш MyOperation
закончится, , то - это когда вы должны использовать performSelectorOnMainThread:
. Ниже я покажу, как это сделать.
2. Отмена NSOperation
-[NSOperationQueue cancelAllOperations]
вызывает метод -[NSOperation cancel]
, в результате чего последующие вызовы -[NSOperation isCancelled]
возвращают YES
. Однако , вы сделали две вещи, чтобы сделать это неэффективным.
Вы используете @synthesize isCancelled
для переопределения метода -isCancelled
NSOperation. Нет причин делать это. NSOperation
уже реализует -isCancelled
вполне приемлемым образом.
Вы проверяете собственную переменную экземпляра _isCancelled
, чтобы определить, была ли операция отменена. NSOperation
гарантирует, что [self isCancelled]
вернет YES
, если операция была отменена. Это не гарантирует, что будет вызван ваш пользовательский метод установки, или что ваша собственная переменная экземпляра актуальна. Вы должны проверять [self isCancelled]
Что вы должны делать
Заголовок:
// MyOperation.h
@interface MyOperation : NSOperation {
}
@end
И реализация:
// MyOperation.m
@implementation MyOperation
- (void)main {
if ([self isCancelled]) {
NSLog(@"** operation cancelled **");
}
// Do some work here
NSLog(@"Working... working....")
if ([self isCancelled]) {
NSLog(@"** operation cancelled **");
}
// Do any clean-up work here...
// If you need to update some UI when the operation is complete, do this:
[self performSelectorOnMainThread:@selector(updateButton) withObject:nil waitUntilDone:NO];
NSLog(@"Operation finished");
}
- (void)updateButton {
// Update the button here
}
@end
Обратите внимание, что вам не нужно ничего делать с isExecuting
, isCancelled
или isFinished
. Все это обрабатывается автоматически для вас. Просто переопределите метод -main
. Это так просто.
(Примечание: технически это не «одновременный» NSOperation
, в том смысле, что -[MyOperation isConcurrent]
вернет NO
, как реализовано выше. Однако, будет работать на фоновый поток. Метод isConcurrent
действительно должен называться -willCreateOwnThread
, так как это более точное описание намерения метода.)