Подclassификация NSOperation будет одновременной и отменой

Я не могу найти хорошую документацию о том, как подclass NSOperation будет одновременным, а также поддерживать отмену. Я читал документы Apple, но я не могу найти «официальный» пример.

Вот мой исходный код:

 @synthesize isExecuting = _isExecuting; @synthesize isFinished = _isFinished; @synthesize isCancelled = _isCancelled; - (BOOL)isConcurrent { return YES; } - (void)start { /* WHY SHOULD I PUT THIS ? if (![NSThread isMainThread]) { [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO]; return; } */ [self willChangeValueForKey:@"isExecuting"]; _isExecuting = YES; [self didChangeValueForKey:@"isExecuting"]; if (_isCancelled == YES) { NSLog(@"** OPERATION CANCELED **"); } else { NSLog(@"Operation started."); sleep(1); [self finish]; } } - (void)finish { NSLog(@"operationfinished."); [self willChangeValueForKey:@"isExecuting"]; [self willChangeValueForKey:@"isFinished"]; _isExecuting = NO; _isFinished = YES; [self didChangeValueForKey:@"isExecuting"]; [self didChangeValueForKey:@"isFinished"]; if (_isCancelled == YES) { NSLog(@"** OPERATION CANCELED **"); } } 

В примере, который я нашел, я не понимаю, почему используется useSelectorOnMainThread:. Это предотвратит запуск моей операции одновременно.

Кроме того, когда я прокомментирую эту строку, я запускаю свои операции одновременно. Однако флаг isCancelled не изменяется, хотя я вызвал cancelAllOperations .

Хорошо, так как я это понимаю, у вас есть два вопроса:

  1. Вам нужен сегмент performSelectorOnMainThread: который появляется в комментариях в вашем коде? Что делает этот код?

  2. Почему флаг _isCancelled не изменяется, когда вы вызываете cancelAllOperations в NSOperationQueue который содержит эту операцию?

Давайте рассмотрим их по порядку. Я собираюсь предположить, что ваш подclass NSOperation называется MyOperation , просто для удобства объяснения. Я объясню, что вы недопонимаете, а затем дайте исправленный пример.

1. Выполнение NSOperations одновременно

В большинстве случаев вы будете использовать NSOperation с NSOperationQueue , и из вашего кода это похоже на то, что вы делаете. В этом случае ваш MyOperation будет всегда запускаться в фоновом streamе, независимо от того, что возвращает метод -(BOOL)isConcurrent , поскольку NSOperationQueue s явно предназначен для выполнения операций в фоновом режиме.

Как правило, вам вообще не нужно переопределять метод -[NSOperation start] , поскольку по умолчанию он просто вызывает метод -main . Это метод, который вы должны переопределить. Метод -start умолчанию уже обрабатывает параметр isExecuting и isFinished для вас в соответствующее время.

Поэтому, если вы хотите, чтобы NSOperation выполнялся в фоновом режиме, просто переопределите метод NSOperationQueue и поместите его в NSOperationQueue .

Функция performSelectorOnMainThread: в вашем коде каждый экземпляр MyOperation всегда будет выполнять свою задачу в основном streamе. Так как только один кусок кода может работать по streamу за раз, это означает, что никакие другие MyOperation s не могут быть запущены. Вся цель NSOperation и NSOperationQueue – сделать что-то в фоновом режиме.

Единственный момент, когда вы хотите заставить вещи на основной stream, – это когда вы обновляете пользовательский интерфейс. Если вам нужно обновить пользовательский интерфейс, когда заканчивается MyOperation , то есть, когда вы должны использовать performSelectorOnMainThread: Я покажу, как это сделать в моем примере ниже.

2. Отмена NSOperation

-[NSOperationQueue cancelAllOperations] вызывает -[NSOperation cancel] , который вызывает последующие вызовы -[NSOperation isCancelled] возвращает YES . Однако вы сделали две вещи, чтобы сделать это неэффективным.

  1. Вы используете @synthesize isCancelled для переопределения метода -isCancelled . Для этого нет оснований. NSOperation уже реализует -isCancelled совершенно приемлемым образом.

  2. Вы проверяете собственную переменную экземпляра _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 как было реализовано выше, но будет запущено в фоновом streamе. Метод isConcurrent действительно должен быть назван -willCreateOwnThread , поскольку это более точное описание намерения метода.)

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

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

Однако, если ваша нагрузка является асинхронной по своей природе, то есть загружая NSURLConnection, вы должны запустить подclass. Когда ваш метод запуска возвращается, операция еще не закончена. Он будет считаться завершенным NSOperationQueue, когда вы вручную отправляете уведомления KVO на флаги isFinished и isExecuting (например, после того, как загрузка URL-адреса async закончится или завершится с ошибкой).

Наконец, можно захотеть отправить начало основному streamу, когда для рабочей нагрузки async, которую вы хотите запустить, требуется прослушивание цикла запуска в основном streamе. Поскольку сама работа является асинхронной, она не будет ограничивать ваш параллелизм, но запуск работы в рабочем streamе может не иметь надлежащей runloop.

Отличный ответ @BJHomer заслуживает обновления.

Параллельные операции должны переопределять метод start вместо main .

Как указано в документации Apple :

Если вы создаете параллельную операцию, вам необходимо переопределить следующие методы и свойства как минимум:

  • start
  • asynchronous
  • executing
  • finished

Для правильной реализации также требуется также отменить cancel . Создание подclassа поточно-безопасным и правильное получение семантики также довольно сложно.

Таким образом, я поставил полный и рабочий подclass в качестве предложения, реализованного в Swift в обзоре кода. Комментарии и предложение приветствуются.

Этот class можно легко использовать в качестве базового classа для вашего пользовательского classа операций.

Взгляните на ASIHTTPRequest . Это class оболочки HTTP, построенный поверх NSOperation в качестве подclassа и, похоже, реализует их. Обратите внимание, что по состоянию на середину 2011 года разработчик рекомендует не использовать ASI для новых проектов.

Что касается определения « отмененного » свойства (или определения « _cancelled » iVAR) в подclassе NSOperation, обычно это НЕ необходимо. Просто потому, что, когда USER запускает отмену, пользовательский код должен всегда уведомлять наблюдателей KVO о том, что ваша операция завершена с его работой. Другими словами, isCancelled => isFinished.

В частности, когда объект NSOperation зависит от завершения других рабочих объектов, он отслеживает путь ключа isFinished для этих объектов. Несоблюдение уведомления о завершении ( в случае отмены ) может поэтому предотвратить выполнение других операций в вашем приложении.


BTW, @BJ Ответ Гомера: «Метод isConcurrent действительно должен быть назван -willCreateOwnThread» делает LOT-смысл !

Поскольку, если вы НЕ переопределяете метод start, просто вручную вызовите метод NSOperation-Object по умолчанию-start-метод, сам вызов-stream по умолчанию является синхронным; поэтому NSOperation-Object – это только неконкурентная операция.

Однако, если вы переопределяете метод start, внутри реализации метода запуска, пользовательский код должен порождать отдельный stream … и т. Д., Тогда вы успешно нарушаете ограничение «синхронный stream вызовов», поэтому NSOperation-Object становится одновременная работа, он может запускаться асинхронно после этого.

Это сообщение в блоге:

http://www.dribin.org/dave/blog/archives/2009/09/13/snowy_concurrent_operations/

объясняет, почему вам может понадобиться:

 if (![NSThread isMainThread]) { [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO]; return; } 

в вашем методе start .

  • Изменить размер для строки состояния вызова?
  • Обнаружение, которое UIButton было нажато в UITableView
  • Push-уведомления в Mavericks iOS Simulator
  • Подclass UIView со своим XIB
  • Сохранить видео Youtube для iPhone в приложении
  • Как создать изображение из UILabel?
  • Основной ключ первичных данных
  • Альтернативы google maps api
  • Передача параметров addTarget: действие: forControlEvents
  • Префикс имен свойств с подчеркиванием в Objective C
  • IPhone записывает видео, которые вращаются в системах Windows
  • Давайте будем гением компьютера.