В методе main, относящемся к операции, необходимо создать собственный автоматически высвобождаемый пул на случай, если когда-нибудь в будущем операция будет добавлена в операционную очередь. Необходимо убедиться в том, что операции можно задействовать обоими способами — как при запуске вручную, так и при запуске в рамках операционной очереди.
У вас должен быть метод-инициализатор для ваших операций. Это обязательно должен быть специальный метод, выделенный под конкретную операцию. Все остальные методы-инициализаторы, в том числе применяемый по умолчанию метод init, должны вызывать вышеупомянутый специальный инициализатор, который содержит наибольшее количество параметров. Другие методы-инициализаторы должны гарантировать, что они передают подходящие параметры методу-инициализатору (если вообще передают).
Вот объявление объекта операции (.h-файл):
#import
@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 = {name = (null), num = 1}
Current Thread = {name = (null), num = 1}
…
Count = 993
Count = 994
Count = 995
Count = 996
Count = 997
Count = 998
Count = 999
Main thread is here
Раздел 7.12.
7.12. Асинхронное выполнение задач с помощью операций
Требуется параллельно выполнять операции.
Воспользуйтесь операционными очередями. В качестве альтернативы можно создавать подклассы от NSOperation и откреплять новый поток в методе main.
Как говорилось в разделе 7.11, операции по умолчанию работают в том потоке, который вызывает метод start. Обычно операции запускаются в основном потоке, но в то же время мы ожидаем, что операции будут выполняться в собственных потоках и, соответственно, не будут тратить процессорное время, уделяемое главному потоку. Наилучшим решением для обеспечения такой работы будет применение операционных очередей. Однако если вы хотите управлять своими операциями вручную, чего бы я не рекомендовал, то можно было бы создавать подклассы от NSOperation и откреплять новый поток в главном методе. Подробнее об открепленных потоках поговорим в разделе 7.15.
Читать дальше
Конец ознакомительного отрывка
Купить книгу