У меня есть NSOperation с запросом AFHTTPClient. В конце операции мне нужно выполнить еще N операций с запросами и дождаться завершения запросов, чтобы пометить основную операцию как завершенную.
@interface MyOperation : OBOperation
@end
@implementation MyOperation
- (id)init
{
if (self = [super init]) {
self.state = OBOperationReadyState;
}
return self;
}
- (void)start
{
self.state = OBOperationExecutingState;
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"http://google.com"]];
[client getPath:@"/"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSOperationQueue *queue = [NSOperationQueue new];
queue.maxConcurrentOperationCount = 1;
NSMutableArray *ops = [NSMutableArray array];
for (int i = 1; i < 10; i++) {
MyInnerOperation *innerOp = [[MyInnerOperation alloc] initWithNumber:@(i)];
[ops addObject:innerOp];
}
[queue addOperations:ops waitUntilFinished:YES];
self.state = OBOperationFinishedState;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
self.state = OBOperationFinishedState;
NSLog(@"error");
}];
}
@end
Ссылка на OBOperation
источник в конце вопроса. Это простой класс, который добавляет полезные методы для управления NSOperation
потоком.
Пример внутренней операции:
@interface MyInnerOperation : OBOperation
- (id)initWithNumber:(NSNumber *)number;
@end
@implementation MyInnerOperation
- (id)initWithNumber:(NSNumber *)number
{
if (self = [super init]) {
_number = number;
self.state = OBOperationReadyState;
}
return self;
}
- (void)start
{
self.state = OBOperationExecutingState;
NSLog(@"begin inner operation: %@", _number);
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"http://google.com"]];
[client getPath:@"/"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"inner operation success: %@", _number);
self.state = OBOperationFinishedState;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
self.state = OBOperationFinishedState;
NSLog(@"inner operation error: %@", _number);
}];
}
@end
Итак, если я начну свою операцию:
MyOperation *op = [MyOperation new];
[_queue addOperation:op];
Вижу в консоли begin inner operation: 1
и все! Мое приложение полностью зависает (даже пользовательский интерфейс)
После некоторых исследований я решил, что зависание вызвано [queue addOperations:ops waitUntilFinished:YES];
. Если я не дождусь завершения, мои внутренние операции будут работать, как и ожидалось, но MyOperation завершится до того, как будут завершены дочерние операции.
Итак, теперь у меня есть обходной путь с операцией зависимого блока:
NSBlockOperation *endOperation = [NSBlockOperation blockOperationWithBlock:^{
self.state = OBOperationFinishedState;
}];
NSMutableArray *ops = [NSMutableArray arrayWithObject:endOperation];
for (int i = 1; i < 10; i++) {
MyInnerOperation *innerOp = [[MyInnerOperation alloc] initWithNumber:@(i)];
[ops addObject:innerOp];
[endOperation addDependency:innerOp];
}
[queue addOperations:ops waitUntilFinished:NO];
Но я до сих пор совершенно не понимаю, в чем реальная проблема этой заморозки. Любое объяснение будет очень полезно.
Источник класса OBOperaton: https://dl.dropboxusercontent.com/u/1999619/issue/OBOperation.h https://dl.dropboxusercontent.com/u/1999619/issue/OBOperation.m
Весь проект: https://dl.dropboxusercontent.com/u/1999619/issue/OperationsTest.zip