Обсуждение

Обсуждение

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

Идем дальше. Попробуем воспользоваться операционной очередью и добавим к ней две простые инициирующие операции (подробнее об инициирующих операциях рассказано в разделе 7.0). Дополнительные примеры кода, описывающие инициирующие операции, имеются в разделе 7.11. Вот объявление (.hm-файл) делегата приложения, в котором используются операционная очередь и две инициирующие операции:

@interface AppDelegate ()

@property (nonatomic, strong) NSOperationQueue *operationQueue;

@property (nonatomic, strong) NSInvocationOperation *firstOperation;

@property (nonatomic, strong) NSInvocationOperation *secondOperation;

@end

@implementation AppDelegate

А вот и внутренняя часть файла реализации делегата приложения:

— (void) firstOperationEntry:(id)paramObject{

NSLog(@"%s", __FUNCTION__);

NSLog(@"Parameter Object = %@", paramObject);

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

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

}

— (void) secondOperationEntry:(id)paramObject{

NSLog(@"%s", __FUNCTION__);

NSLog(@"Parameter Object = %@", paramObject);

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

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

}

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

NSNumber *firstNumber = @111;

NSNumber *secondNumber = @222;

self.firstOperation =[[NSInvocationOperation alloc]

initWithTarget: self

selector:@selector(firstOperationEntry:)

object: firstNumber];

self.secondOperation = [[NSInvocationOperation alloc]

initWithTarget: self

selector:@selector(secondOperationEntry:)

object: secondNumber];

self.operationQueue = [[NSOperationQueue alloc] init];

/* Добавляем операции в очередь. */

[self.operationQueue addOperation: self.firstOperation];

[self.operationQueue addOperation: self.secondOperation];

NSLog(@"Main thread is here");

self.window = [[UIWindow alloc] initWithFrame:

[[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

Вот что происходит в реализации данного кода.

У нас есть два метода, firstOperationEntry: и secondOperationEntry:. Каждый из этих методов принимает в качестве параметра объект и выводит в окне консоли информацию об актуальном потоке, главном потоке и этом параметре. Это входные методы инициирующих операций, которые будут добавляться в операционную очередь.

Мы инициализируем два метода типа NSInvocationOperation и задаем целевой селектор в точке входа каждой операции (эти точки входа были описаны выше).

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

Итак, еще раз запустим код примера и посмотрим, что же у нас на консоли:

[Running_Tasks_Asynchronously_with_OperationsAppDelegate firstOperationEntry: ]

Main thread is here

Parameter Object = 111

[Running_Tasks_Asynchronously_with_OperationsAppDelegate secondOperationEntry: ]

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

Parameter Object = 222

Current Thread = <NSThread: 0x6805c20>{name = (null), num = 3}

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

Current Thread = <NSThread: 0x6b2d1d0>{name = (null), num = 4}

Блестяще! Это доказывает, что инициирующие операции параллельно выполняются каждая в своем потоке и в то же время параллельно главному потоку, вообще не блокируя его. Теперь еще пару раз прогоним этот же код и посмотрим, какой вывод будет появляться в окне консоли. В таком случае вы можете получить совершенно иной результат, например:

Main thread is here

[Running_Tasks_Asynchronously_with_OperationsAppDelegate firstOperationEntry: ]

[Running_Tasks_Asynchronously_with_OperationsAppDelegate secondOperationEntry: ]

Parameter Object = 111

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

Current Thread = <NSThread: 0x68247c0>{name = (null), num = 3}

Parameter Object = 222

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

Current Thread = <NSThread: 0x6819b00>{name = (null), num = 4}

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

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

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

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

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

Переопределите методы isFinished и isExecuting операции и верните соответствующие логические (BOOL) значения, показывающие, завершена операция или продолжается в настоящий момент.

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

#import <Foundation/Foundation.h>

@interface SimpleOperation: NSOperation

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

— (id) initWithObject:(NSObject *)paramObject;

@end

Реализация операции такова:

#import «SimpleOperation.h»

@implementation SimpleOperation

— (instancetype) init {

return([self initWithObject:@123]);

}

— (instancetype) initWithObject:(NSObject *)paramObject{

self = [super init];

if (self!= nil){

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

_givenObject = paramObject;

}

return(self);

}

— (void) main {

@try {

@autoreleasepool {

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

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

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

BOOL taskIsFinished = NO;

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

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

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

while (taskIsFinished == NO &&

[self isCancelled] == NO){

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

NSLog(@"%s", __FUNCTION__);

NSLog(@"Parameter Object = %@", givenObject);

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

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

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

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

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);

}

}

— (BOOL) isConcurrent{

return YES;

}

@end

Теперь этот операционный класс можно использовать в любом другом классе, например в делегате вашего приложения. Вот объявление делегата приложения, в котором задействуется этот новый класс операции, добавляемый в операционную очередь:

@interface AppDelegate ()

@property (nonatomic, strong) NSOperationQueue *operationQueue;

@property (nonatomic, strong) SimpleOperation *firstOperation;

@property (nonatomic, strong) SimpleOperation *secondOperation;

@end

@implementation AppDelegate

Реализация делегата приложения такова:

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

NSNumber *firstNumber = @111;

NSNumber *secondNumber = @222;

self.firstOperation = [[SimpleOperation alloc]

initWithObject: firstNumber];

self.secondOperation = [[SimpleOperation alloc]

initWithObject: secondNumber];

self.operationQueue = [[NSOperationQueue alloc] init];

/* Добавляем операции в очередь. */

[self.operationQueue addOperation: self.firstOperation];

[self.operationQueue addOperation: self.secondOperation];

NSLog(@"Main thread is here");

self.window = [[UIWindow alloc] initWithFrame:

[[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

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

Main thread is here

-[SimpleOperation main]

-[SimpleOperation main]

Parameter Object = 222

Parameter Object = 222

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

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

Current Thread = <NSThread: 0x6a10b90>{name = (null), num = 3}

Current Thread = <NSThread: 0x6a13f50>{name = (null), num = 4}

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



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

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

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

Обсуждение

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

Обсуждение Обычно после того, как пользователь успешно снимет фотографию на устройство с 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) на устройстве. Чтобы лучше понять, как работает этот механизм,


Обсуждение

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

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