Представьте себе ситуацию, когда вам нужно асинхронно выполнить несколько задач (загрузка, вычисление и т.д.) с некоторым приоритетом и зависимостью друг от друга. Или синхронно? Или дождаться окончания одной из таких операций. Далее пойдет речь именно об этом.

Операция

Под операцией будем понимать какую-либо задачу, как я писал выше — задача загрузки контента или обработка картинки, вобщем, задачу, которая занимает продолжительное время. Такую задачу нежелательно запускать в главном потоке, в котором работает UIKit — интерфейс попросту перестанет реагировать на действия пользователя до тех пор, пока задача не закончится.

Что же делать?

Очевидный и самый быстрый ответ на этот вопрос — создать отдельный поток и «решить» задачу в нем. Согласен, эта идея имеет право на существование. Но создавая потоки мы не можем выставить зависимость выполнения одного от другого. Под зависимостью я понимаю следующее: операция B не может начаться, пока не завершилась операция А и С, а операция С не может начаться пока не завершится А. К тому же если нужно будет запускать потоки по-очереди придется изобрести свой маленький «велосипед».

NSOperation

В Foundation есть абстрактный класс NSOperation, который позволяет «обернуть» задачу в класс и выполнить операцию (задачу). Кроме абстрактного NSOperation существуют еще NSBlockOperation и NSInvocationOperation. В чем разница? При использовании абстрактного класса NSOperation нам нужно создавать отдельный класс, унаследовать NSOperation и реализовать задачу. NSBlockOperation позволяет «обернуть» один или несколько блоков кода, т.е. нет необходимости наследования. NSInvocationOperation позволяет создать операцию из уже существующего метода. Операции могут быть асинхонными (non-concurrent) и синхронными (concurrent). У слова concurrent/non-concurrent есть другой перевод, но мне кажется синхронный/асинхронный понятнее.

Запуск операций

Существует два метода запуска операций на выполнение.

1) NSOperationQueue

2) Запуск вручную

При использовании NSOperationQueue учитывается зависимость операций и приоритет, а так же обеспечивается асинхронность операции. Очереди можно приостанавливать и снова запускать.

Если мы запускаем задачу «вручную» то она выполняется синхронно, блокируя поток вызова до тех пор, пока операция не закончится.

Создание и выполнение операции

Создадим операцию при помощи наследования от NSOperation. Для этого создадим класс MyOperation:

MyOperation.h

#import <foundation/Foundation.h>

@interface MyOperation : NSOperation {
}
@end

MyOperation.m

#import "MyOperation.h"

@implementation MyOperation
-(void) main
{
// задача которая требует некоторое кол-во времени
// поток заснет на некоторое время
sleep(3);
}
@end

Обязательным условием является определение метода main в классе. Именно он будет выполняться асинхронно. Так же можно определить свой конструктор, например, для передачи каких-либо параметров в операцию. Можно добавить свойства.

Как я говорил выше, одним из способов запуска операций является NSOperationQueue. Делается это так:

NSOperationQueue* queue = [NSOperationQueue new];

MyOperation* op = [MyOperation new];
[queue addOperation:op];

Ручной запуск

«Вручную» запустить операцию очень просто. Достаточно вызвать у нее метод start. Операция начнет немедленно выполняться и заблокирует поток вызова до момента своего завершения.

Синхронные и асинхронные операции

Как я уже писал, создавая асинхронную операцию нам нужно определить метод main. Когда мы создаем синхронную операцию мы должны создать методы:

-(void) start;

-(BOOL) isExecuting;
-(BOOL) isFinished;
-(BOOL) isConcurrent;

Метод start должен содержать код задачи. Остальные три сообщают о своем соостоянии, что видно из названия. IsConcurrent возвращает YES если задача выполняется асинхронно в отношении вызвашего (метод) потока, NO — если синхронно в отношении вызвавшего операцию потока.

Асинхронные операции сами реагируют на вызов методов isExecuting, isFinished, isConcurrent

Безопасность

Методы класса NSOperation являются потокобезопасными. Поэтому можно вызывать методы без дополнительных проверок и локов. Сделано это для того, чтобы выполнение операции можно было мониторить из других потоков.

Немного другая ситуация в тех методах которые мы переопределяем, когда наследуем NSOperation. В них нам самим нужно заботиться о потокобезопасности и раздельного доступа к данным.

Зависимости

Зависимости для операций (которые выполняются в NSOperationQueue) выставляются достаточно просто, существует два метода у NSOperation — установка зависимости и ее удаление:

- (void)addDependency:(NSOperation *)operation

- (void)removeDependency:(NSOperation *)operation

Можно также узнать весь список зависимостей:

- (NSArray *)dependencies

Итог

Любая операция выполняет свою, четко поставленную задачу. Запускать задачи можно либо вручную, либо через NSOperationQueue (очередь операций). Очередь операций дает возможность выполнять операции асинхронно, узнавать из состояние, устанавливать зависимости и другое.

Более подробно на английском языке можно прочитать тут

Загрузить проект можно тут

Похожие статьи

  • Audio Unit в iOS. Часть 3, накладываем эффект Delay

  • Audio Unit в iOS. Часть 2, строим граф и проигрываем файлы

  • Audio Unit в iOS. Часть 1, введение.

  • Используем Emoji в своих приложениях

  • Как вводить в UITextField только цифры?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *