По умолчанию операция выполняется в том потоке, который ее начал с помощью их метода экземпляра start. Если вы хотите, чтобы операции выполнялись асинхронно, нужно использовать либо очередь операций, либо подкласс NSOperation и выделить новый поток в методе экземпляра main, относящегося к операции.
• Операция может дождаться окончания выполнения другой операции и только после этого начаться. Будьте осторожны и не создавайте взаимозависимых операций. Такая распространенная ошибка называется
• Операции можно отменять. Так, если вы создаете подклассы от NSOperation, чтобы делать собственные виды объектов операций, обязательно пользуйтесь методом экземпляра isCancelled. Он применяется, чтобы проверить, не была ли отменена определенная операция, прежде чем переходить к выполнению задачи, связанной с этой операцией. Например, если задача вашей операции — проверять доступность соединения с Интернетом раз в 20 с, то перед каждым запуском операции нужно вызвать метод экземпляра isCancelled, чтобы сначала убедиться, что операция не отменена, и только после этого пытаться проверять наличие соединения с Интернетом. Если на выполнение операции уходит более нескольких секунд (например, если это загрузка файла), то при выполнении задачи нужно также периодически проверять метод isCancelled.
• Объекты операций обязаны выполнять «уведомление наблюдателей об изменениях в свойствах наблюдаемого объекта» (KVO, Key-Value Observing) на различных ключевых путях, в частности isFinished, isReady и isExecuting. В одной из следующих глав мы обсудим механизм KVO, а также KVC — механизм для доступа к полям объекта по именам этих полей.
• Если вы планируете создавать подкласс от NSOperation и выполнять специальную реализацию для операции, вам следует создать собственный автоматически высвобождаемый пул в методе main, относящемся к операции. Данный метод вызывается из метода start. Эти вопросы мы подробнее рассмотрим далее в этой главе.
• Всегда сохраняйте ссылки на создаваемые вами объекты операций. Сама параллельная природа, присущая очередям операций, исключает возможность получения ссылки на операцию после того, как она добавлена в очередь.
Потоки и таймеры — это объекты, являющиеся подклассами от NSObject. Для порождения потока выполняется больше работы, чем для создания таймеров, а настройка цикла потока — более сложная задача, чем обычное слушание таймера, запускающего селектор. Когда приложение работает в операционной системе iOS, система создает для этого приложения как минимум один поток. Этот поток называется главным (Main Thread). Все потоки и таймеры должны добавляться в цикл исполнения (Run Loop). Цикл исполнения, как понятно из его названия, — это цикл, в ходе которого могут происходить разные события, например запуск таймера или выполнение потока. Обсуждение циклов исполнения выходит за рамки этой главы, но иногда я буду упоминать такой цикл.
Цикл исполнения — это, в сущности, обычный цикл, у которого есть начальная точка, условие завершения и серия событий, которые необходимо обработать в ходе этого цикла. Поток или таймер прикрепляются к циклу исполнения, и, в сущности, именно они заставляют цикл исполнения работать.
Главный поток приложения — это тот поток, который обрабатывает события пользовательского интерфейса. Если вы выполняете в главном потоке долговременную задачу, то быстро станет заметно, что интерфейс перестает отвечать на запросы или реагирует медленно. Во избежание этого можно создавать отдельные потоки и/или таймеры, каждый из которых выполняет собственную задачу (даже если она сравнительно долговременная). Но при этом главный поток не будет блокироваться.
7.1. Создание блоковых объектов
Постановка задачи
Необходимо иметь возможность писать собственные блоковые объекты либо использовать блоковые объекты с классами из iOS SDK.
Решение
Просто необходимо понимать базовую разницу между синтаксисом блоковых объектов и синтаксисом классических функций языка C. Эта разница рассматривается в подразделе «Обсуждение» данного раздела.
Обсуждение
Блоковые объекты могут быть либо встраиваемыми, либо записываемыми как отдельные блоки кода. Начнем с объектов второго типа. Предположим, у нас есть метод языка Objective-C, принимающий два целочисленных значения типа NSInteger и возвращающий разницу двух этих значений в форме NSInteger. Разница получается в результате вычитания одного значения из другого:
— (NSInteger) subtract:(NSInteger)paramValue
from:(NSInteger)paramFrom{
return paramFrom — paramValue;
}