NSOperationQueue waittillalloperationsarefinished не работает в фоновом режиме
Приложение, над которым я работаю, периодически обновляет локальный кэш данных с сервера приложений (10+ запросов, каждый из которых занимает довольно много времени). В настоящее время я выполняю эти запросы асинхронно, чтобы не блокировать поток пользовательского интерфейса. Поскольку эти запросы требуют некоторого времени для обработки и последующей загрузки в основные данные, я хотел бы использовать beginBackgroundTaskWithExpirationHandler
и зависимое поведение операций NSOperationQueue
.
После того, как я добавил все свои запросы в очередь операций, я использую waitUntilAllOperationsAreFinished
для блокировки до тех пор, пока все операции не будут завершены (это не в главном потоке). Проблема, которую я вижу в своем прототипе, заключается в том, что когда я запускаю приложение и сразу же помещаю его в фоновый режим (нажимаю кнопку home), waitUntilAllOperationsAreFinished
остается заблокированным даже после завершения всех операций... но как только я снова открываю приложение, обработчик завершает работу. Если я запускаю приложение и оставляю его на переднем плане, все заканчивается хорошо. Такое поведение не всегда происходит, когда в моем реальном приложении, но с примером код ниже, кажется:
#import "ViewController.h"
@interface ViewController ()
@property (assign, nonatomic) UIBackgroundTaskIdentifier task;
@property (strong, nonatomic) NSOperationQueue *queue;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelectorInBackground:@selector(queueItUp) withObject:nil];
}
- (void)queueItUp {
UIApplication *application = [UIApplication sharedApplication];
self.queue = [[NSOperationQueue alloc] init];
self.task = [application beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"Took too long!");
[self.queue cancelAllOperations];
[application endBackgroundTask:self.task];
self.task = UIBackgroundTaskInvalid;
}];
for (int i = 0; i < 5; i++) {
[self.queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:3];
NSLog(@"Finished operation.");
}];
}
NSLog(@"Waiting until all operations are finished.");
[self.queue waitUntilAllOperationsAreFinished];
[application endBackgroundTask:self.task];
self.task = UIBackgroundTaskInvalid;
NSLog(@"All done :)");
}
@end
Что я делаю не так?
Спасибо
2 ответа:
Ваш пример кода не блокируется постоянно в любой точке iOS 6.1 на iPhone или симуляторе. Поставьте точку останова на линии
[application endBackgroundTask:self.task];
, и вы будете попадать в нее каждый раз.Поведение, которое вы видите, связано с тем, что вы регистрируете в фоновом режиме после того, как вы сказали приложению завершить фоновую задачу. Я не уверен в точных деталях, но журналы каким-то образом помещаются в очередь для печати на консоль после восстановления вашего приложения на передний план. Возможно, так оно и есть. вместо этого выполнение потока приостанавливается.
Если вы переместите ваш
NSLog(@"All done");
до перед вызовом вendBackgroundTask:
, вы увидите протоколированный вывод.
Используя Ваш пример кода, операции завершаются просто отлично. Это на
NSLog(@"All done :)");
, что вы висите. Ваша очередь все еще существует и не имеет ожидающих операций, но у вас больше не может быть активного runloop, и основной поток блокируется как вы, так и в фоновом режиме. Поскольку вы завершили ожидающие фоновые операции, вы блокируетесь. Когда вы возобновляете свое приложение, оно продолжает с того места, на котором остановилось. Если вы сделаете это:Поведение должно быть еще более очевидным. Оно делает именно то, что вы говорите ему делать.[[self queue] addOperationWithBlock:^{ NSLog(@"All done :)"); }];
У вас есть куча операций в очереди, и вы вызвали
waitUntilAllOperationsAreFinished
. Когда они заканчивают, вы блокируетесь в фоновом режиме.Похоже, что вы пытаетесь выполнить что-то, когда эта группа операций закончена. NSOperation предоставляет возможность иметь зависимости и блоки завершения, которые позволяют построить такой тип поведения. Вы можете сгруппировать операции и установить блок завершения или операцию, которая выполняется, когда ваша группа операции завершены. Некоторые из них описаны в руководстве по программированию параллелизма