Подклассный NSOperation должен быть одновременно и отменен
-
27-09-2019 - |
Вопрос
Я не могу найти хорошую документацию о том, как подкласс 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 **");
}
}
В примере я нашел, я не понимаю, почему используется ServiceSelectoronMaintHaintHeaint: используется. Это помешало бы моей операции работать одновременно.
Кроме того, когда я прокомментую эту линию, я получаю свою операцию одновременно. Однако isCancelled
Флаг не изменен, даже если я позвонил cancelAllOperations
.
Решение
Хорошо, так как я понимаю, у вас есть два вопроса:
Вам нужен
performSelectorOnMainThread:
Сегмент, который появляется в комментариях в вашем коде? Что делает этот код?Почему то
_isCancelled
Флаг не изменен, когда вы звонитеcancelAllOperations
наNSOperationQueue
который содержит эту операцию?
Давайте имеем дело с этим в порядке. Я собираюсь предположить, что ваш подкласс NSOperation
называется MyOperation
, Просто для простоты объяснения. Я объясню, что вы недоразумеете, а затем даете исправленный пример.
1. Запуск NsoPerations одновременно
Большую часть времени вы будете использовать NSOperation
с an. NSOperationQueue
, И из вашего кода это звучит так, как вы делаете. В этом случае ваш MyOperation
всегда будет работать на фоновой нити, независимо от того, что -(BOOL)isConcurrent
метод возвращается, поскольку NSOperationQueue
s явно разработаны для запуска операций на заднем плане.
Как таковой, вам вообще не нужно переопределять -[NSOperation start]
метод, поскольку по умолчанию он просто вызывает -main
метод. Это метод, который вы должны быть переопределены. По умолчанию -start
Метод уже обрабатывает настройку isExecuting
а также isFinished
для вас в соответствующие времена.
Так что если вы хотите NSOperation
бежать на заднем плане, просто переопределите -main
метод и положить его на NSOperationQueue
.
То performSelectorOnMainThread:
в вашем коде вызвало бы каждый случай MyOperation
всегда выполнять свою задачу на главной ните. Поскольку только один кусок кода может работать в потоке за раз, это означает, что никто другой MyOperation
S может быть запущен. Вся цель NSOperation
а также NSOperationQueue
это сделать что-то на заднем плане.
Единственный раз, когда вы хотите заставить вещи на основной нитью, заключается в том, когда вы обновляете пользовательский интерфейс. Если вам нужно обновить пользовательский интерфейс, когда ваш MyOperation
заканчивается, это когда вы должны использовать performSelectorOnMainThread:
. Отказ Я покажу, как это сделать в моем примере ниже.
2. Отмена Nsoperation
-[NSOperationQueue cancelAllOperations]
называет -[NSOperation cancel]
метод, который вызывает последующие вызовы -[NSOperation isCancelled]
возвращать YES
. Однако, Вы сделали две вещи, чтобы сделать это неэффективным.
Ты используешь
@synthesize isCancelled
переопределить Nsoperation-isCancelled
метод. Нет причин сделать это.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
, Как это точнее описание намерения метода.)
Другие советы
Отличный ответ @bjhomer заслуживает обновления.
Одновременные операции должны переопределять start
Метод вместо main
.
Как указано в Документация Apple:
Если вы создаете параллельную работу, вам необходимо переопределить следующие методы и свойства как минимум:
start
asynchronous
executing
finished
Правильная реализация также требует переопределить cancel
также. Сделать подкласс Безопасен в потоке И получение необходимой семантики вправо также довольно сложно.
Таким образом, я поставил полный и рабочий подкласс как Предложение реализовано в Swift в обзоре кода. Комментарии и предложения приветствуются.
Этот класс можно легко использовать в качестве базового класса для вашего пользовательского класса операций.
Я знаю, что это старый вопрос, но я расследую это в последнее время и столкнулся с теми же примерами и имел одинаковые сомнения.
Если вся ваша работа может быть запущена синхронно внутри основного метода, вам не нужна одновременная операция, ни единое начало, просто выполняйте свою работу и вернемся от Main, когда это сделано.
Однако, если ваша рабочая нагрузка асинхронная по природе - то есть загрузка NSURLConnection, вы должны запустить подкласс. Когда ваш метод запуска возвращает, операция еще не закончена. Он будет рассмотрен только завершенным NsoPerationQueue, когда вы вручную отправляете уведомления KVO в установленные флаги IFINIFIED и ISEXECUTING (например, после того, как async Url загрузка заканчивается или не удается).
Наконец, можно захотеть отправить начать главный поток, когда async Readload, которую вы хотите начать, требуется прослушивание прослушивания на главной ните. Когда сама работа асинхронана, она не ограничивает вашу параллелизм, но запуск работы в рабочей ните не может иметь правильного RunLoop Ready.
Взгляни на ASIHTTPREQUEST.. Отказ Это класс HTTP Wrapper, построенный поверх NSOperation
в качестве подкласса и, кажется, реализует их. Обратите внимание, что по состоянию на середину 2011 года разработчик рекомендует не использовать ASI для новых проектов.
Что касается определения "отменил«Собственность (или определить»_Cancelled«Ивар) внутри подкласса NSOperation, обычно не нужен. Просто потому, когда пользователь запускает аннулирование, пользовательский код всегда должен уведомить наблюдателей KVO, что ваша операция сейчас законченный с его работой. Другими словами, ISCancelled => устанавливается.
Особенно, когда объект NSOperation зависит от завершения других объектов операций, оно следит за ослабленным ключевым путем для этих объектов. Неспособность генерировать уведомление отделки (В случае отмены случается) может поэтому предотвратить выполнение других операций в вашем приложении.
Кстати, ответ @bj Homer: «Метод Isconcurrent действительно должен быть названо -WillCreateownThread "имеет большой смысл!
Потому что, если вы не переопределите старт-метод, просто вручную вызовите метод START-MANT-START, вызов NSOperation-Object, вызова сама поток, по умолчанию, синхронно; Итак, Nsoperation - объект - это только не одновременно.
Тем не менее, если вы выполняете переопределенную старт-метод, внутри внедрения START-метода, пользовательский код должен порождать отдельная нить... и т. Д., Тогда вы успешно разбили ограничение «Calling-Thread по умолчанию, синхронному», поэтому делая NSOperation-объект становится одновременным операцией, он может работать асинхронно после.
Этот блог пост:
http://www.dribin.org/dave/blog/archives/2009/09/13/soney_concurrent_Operations/
Объясняет, почему вам может понадобиться:
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
в твоем start
метод.