Подклассный NSOperation должен быть одновременно и отменен

StackOverflow https://stackoverflow.com/questions/3859631

Вопрос

Я не могу найти хорошую документацию о том, как подкласс 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.

Это было полезно?

Решение

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

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

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

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

1. Запуск NsoPerations одновременно

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

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

Так что если вы хотите NSOperation бежать на заднем плане, просто переопределите -main метод и положить его на NSOperationQueue.

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

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

2. Отмена Nsoperation

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

  1. Ты используешь @synthesize isCancelled переопределить Nsoperation -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 как реализовано выше. Однако это буду Бегите на фоновой резьбе. То 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 метод.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top