Обсуждение

Обсуждение

Операция активизации, как объяснялось в разделе 7.0, позволяет активизировать метод в объекте. «Что же в этом особенного?» — спросите вы. Потенциал активизирующей операции можно продемонстрировать, когда такая операция добавляется в операционную очередь. Примененная вместе с операционной очередью, активизирующая операция может асинхронно запустить метод в заданном объекте параллельно тому потоку, который начал операцию. Внимательно рассмотрев вывод с консоли (приведенный в подразделе «Решение» данного раздела), вы заметите, что актуальный поток в методе, запущенный активизирующей операцией, равен главному потоку. Действительно, главный поток в методе application: didFinishLaunchingWithOptions: запускает операцию, пользуясь ее методом start. В разделе 7.12 мы научимся эффективно использовать операционные очереди для асинхронного выполнения задач.

Кроме активизирующих операций, вы можете применять блоковые или обычные операции для синхронного выполнения задач. Вот пример использования блоковой операции для подсчета чисел от 0 до 999 (это происходит в. h-файле делегата приложения):

@interface AppDelegate ()

@property (nonatomic, strong) NSBlockOperation *simpleOperation;

@end

@implementation AppDelegate

А вот реализация делегата приложения (.m-файл):

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

self.simpleOperation = [NSBlockOperation blockOperationWithBlock: ^{

NSLog(@"Main Thread = %@", [NSThread mainThread]);

NSLog(@"Current Thread = %@", [NSThread currentThread]);

NSUInteger counter = 0;

for (counter = 0;

counter < 1000;

counter++){

NSLog(@"Count = %lu", (unsigned long)counter);

}

}];

/* Запуск операции. */

[self.simpleOperation start];

/* Выводим что-нибудь на консоль, просто чтобы проверить,

должны мы дожидаться, пока выполнится блок кода, или нет.*/

NSLog(@"Main thread is here");

self.window = [[UIWindow alloc] initWithFrame:

[[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

Если запустить приложение, мы увидим, что на экране выводятся значения от 0 до 999, а за ними следует сообщение Main thread is here (Это главный поток):

Main Thread = <NSThread: 0x68 10280>{name = (null), num = 1}

Current Thread = <NSThread: 0x68 10280>{name = (null), num = 1}

Count = 991

Count = 992

Count = 993

Count = 994

Count = 995

Count = 996

Count = 997

Count = 998

Count = 999

Main thread is here

Итак, убеждаемся, что, поскольку блоковая операция была запущена в методе application: didFinishLaunchingWithOptions:, который сам работает в главном потоке, код внутри блока также выполняется в главном потоке. Основные сведения, которые мы получаем из этих регистрационных записей (логов), сводятся к следующему: операция блокировала главный поток, и потребовалось вернуться к выполнению кода основного потока после того, как была завершена работа блоковой операции. Это образец очень непрофессионального программирования. На самом деле программисты, работающие с iOS, должны идти на любые уловки и пользоваться любыми известными им приемами, чтобы обеспечивать отклик основного потока в любой момент и чтобы этот поток мог заниматься своим основным делом — обработкой пользовательского ввода. Вот что об этом пишет Apple.

«Необходимо внимательно отслеживать, какие задачи вы решаете в главном потоке вашего приложения. Именно в главном потоке ваша программа отрабатывает события касания и другой пользовательский ввод. Чтобы гарантировать, что приложение в любой момент будет откликаться на действия пользователя, никогда не следует загружать главный поток выполнением долговременных задач либо выполнением задач с потенциально неопределенным концом. Таковы, в частности, задачи, связанные с доступом к сети. Напротив, подобные задачи следует решать в фоновых потоках. Оптимальный способ решения таких задач — заключение их в объект операции и добавление этого объекта к операционной очереди. Но вы можете и сами создавать потоки вручную».

Подробнее эта тема рассматривается в документе Performance Tuning («Повышение производительности») в справочной библиотеке iOS. Документ расположен по адресу https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/PerformanceTuning/PerformanceTuning.html#//apple_ref/doc/uid/TP400 07072-CH8-SW1.

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

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

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

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

У вас должен быть метод-инициализатор для ваших операций. Это обязательно должен быть специальный метод, выделенный под конкретную операцию. Все остальные методы-инициализаторы, в том числе применяемый по умолчанию метод init, должны вызывать вышеупомянутый специальный инициализатор, который содержит наибольшее количество параметров. Другие методы-инициализаторы должны гарантировать, что они передают подходящие параметры методу-инициализатору (если вообще передают).

Вот объявление объекта операции (.h-файл):

#import <Foundation/Foundation.h>

@interface CountingOperation: NSOperation

/* Выделенный инициализатор */

— (id) initWithStartingCount:(NSUInteger)paramStartingCount

endingCount:(NSUInteger)paramEndingCount;

@end

Реализация операции (записываемая в. m-файле) несколько длинновата, но, надеюсь, вполне понятна:

#import «CountingOperation.h»

@implementation CountingOperation

@property (nonatomic, unsafe_unretained) NSUInteger startingCount;

@property (nonatomic, unsafe_unretained) NSUInteger endingCount;

@property (nonatomic, unsafe_unretained, getter=isFinished) BOOL finished;

@property (nonatomic, unsafe_unretained, getter=isExecuting) BOOL executing;

@end

@implementation CountingOperation

— (instancetype) init {

return([self initWithStartingCount:0

endingCount:1000]);

}

— (instancetype) initWithStartingCount:(NSUInteger)paramStartingCount

endingCount:(NSUInteger)paramEndingCount{

self = [super init];

if (self!= nil){

/* Сохраните эти значения для главного метода. */

startingCount = paramStartingCount;

endingCount = paramEndingCount;

}

return(self);

}

— (void) main {

@try {

/* Это автоматически высвобождаемый пул. */

@autoreleasepool {

/* Сохраняем здесь локальную переменную, которая

должна быть установлена в YES всякий раз, когда

мы завершаем выполнение задачи. */

BOOL taskIsFinished = NO;

/* Создаем здесь цикл while, существующий лишь в том случае,

когда переменная taskIsFinished устанавливается в YES

или операция отменяется. */

while (taskIsFinished == NO &&

[self isCancelled] == NO){

/* Здесь выполняется задача. */

NSLog(@"Main Thread = %@", [NSThread mainThread]);

NSLog(@"Current Thread = %@", [NSThread currentThread]);

NSUInteger counter = startingCount;

for (counter = startingCount;

counter < endingCount;

counter++){

NSLog(@"Count = %lu", (unsigned long)counter);

}

/* Очень важно. Здесь мы можем выйти из цикла, по-прежнему

соблюдая правила, по которым отменяются операции. */

taskIsFinished = YES;

}

/* Соответствие KVO. Генерируем требуемые уведомления KVO. */

[self willChangeValueForKey:@"isFinished"];

[self willChangeValueForKey:@"isExecuting"];

finished = YES;

executing = NO;

[self didChangeValueForKey:@"isFinished"];

[self didChangeValueForKey:@"isExecuting"];

}

}

@catch (NSException * e) {

NSLog(@"Exception %@", e);

}

}

@end

Операцию можно начать так:

@interface AppDelegate ()

@property (nonatomic, strong) CountingOperation *simpleOperation;

@end

@implementation AppDelegate

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

self.simpleOperation = [[CountingOperation alloc] initWithStartingCount:0

endingCount:1000];

[self.simpleOperation start];

NSLog(@"Main thread is here");

self.window = [[UIWindow alloc] initWithFrame:

[[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

@end

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

Main Thread = <NSThread: 0x68 10260>{name = (null), num = 1}

Current Thread = <NSThread: 0x68 10260>{name = (null), num = 1}

Count = 993

Count = 994

Count = 995

Count = 996

Count = 997

Count = 998

Count = 999

Main thread is here

Данный текст является ознакомительным фрагментом.



Поделитесь на страничке

Следующая глава >

Похожие главы из других книг:

Обсуждение

Из книги автора

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


Обсуждение

Из книги автора

Обсуждение Фреймворк Assets Library — удобный посредник между разработчиком и библиотекой фотографий. Как будет указано в разделе 13.6, в iOS SDK вам предоставляются встроенные компоненты графического пользовательского интерфейса, которыми можно пользоваться для доступа к


Обсуждение

Из книги автора

Обсуждение Чтобы пользователь мог выбирать фотоснимки или видеоролики из своей библиотеки фотографий, необходимо установить свойство sourceType экземпляра UIImagePickerController в значение UIImagePickerControllerSourceTypePhotoLibrary и только потом открывать перед пользователем инструмент для выбора


Обсуждение

Из книги автора

Обсуждение Библиотека ресурсов подразделяется на группы. В каждой группе содержатся ресурсы, а каждый ресурс имеет свойства, например URL (универсальные локаторы ресурсов) и объекты представления.Все ресурсы всех типов можно получать из библиотеки ресурсов с помощью


Обсуждение

Из книги автора

Обсуждение Класс UIVideoEditorController, содержащийся в iOS SDK, позволяет программисту вывести на экран перед пользователем специальный интерфейс для редактирования. Все, что требуется сделать, — предоставить URL видеоролика, который предполагается отредактировать, а потом


Обсуждение

Из книги автора

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


Обсуждение

Из книги автора

Обсуждение Когда приложение переходит в фоновый режим, работа его основного потока приостанавливается. Потоки, которые вы создаете в своем приложении с помощью метода класса detachNewThreadSelector: toTarget: withObject:, относящегося к классу NSThread, также приостанавливаются. Если вы


Обсуждение

Из книги автора

Обсуждение Многие приложения, ежедневно поступающие на рынок App Store, обладают возможностями соединения с теми или иными серверами. Некоторые выбирают с сервера данные для обновления, другие отсылают информацию на сервер и т. д. В течение долгого времени в iOS существовал


Обсуждение

Из книги автора

Обсуждение В iOS приложение может запросить продолжить воспроизведение своих аудиофайлов, даже если оно само переходит в фоновый режим. В этом разделе мы воспользуемся плеером AVAudioPlayer, который прост и удобен в обращении. Наша задача — запустить аудиоплеер и воспроизвести


Обсуждение

Из книги автора

Обсуждение Когда приложение работает в приоритетном режиме, можно получать от экземпляра CLLocationManager делегатные сообщения, информирующие вас о том, что iOS обнаружила перемещение устройства на новое место. Однако если приложение переходит в фоновый режим и становится


Обсуждение

Из книги автора

Обсуждение Допустим, пустое приложение iOS (то есть приложение всего с одним окном, для которого еще не написан код) впервые запускается на устройстве с iOS, поддерживающем работу в многозадачном режиме. Оно запускается именно впервые, а не возвращается из фонового режима в


Обсуждение

Из книги автора

Обсуждение При работе с приложениями, которые используют класс NSURLConnection, но, уходя в фоновый режим, не запрашивают у iOS дополнительного времени, обращаться с соединениями не составляет никакого труда. Рассмотрим на примере, как будет действовать асинхронное соединение,


Обсуждение

Из книги автора

Обсуждение Пока ваше приложение работает в фоновом режиме, может произойти многое! Например, пользователь может вдруг изменить локализацию устройства с iOS на странице Settings (Настройки) и задать, к примеру, испанский язык вместо английского. Приложения могут


Обсуждение

Из книги автора

Обсуждение В приложениях, написанных для iOS, файл пакета настроек может быть предоставлен пользователю для внесения собственных настроек. Эти настройки будут доступны пользователю в приложении (Settings) на устройстве. Чтобы лучше понять, как работает этот механизм,


Обсуждение

Из книги автора

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