NSOperationQueue addOperation: операция завершена и не может быть поставлена в очередь?
Замечания:
Это просто подтверждение концепции.
Реальная фоновая задача будет заключаться в том, чтобы постоянно запрашивать "HTTP Get" необработанных данных и отображать их через основной поток; по запросу, по требованию.
Сценарий:
1) Переключить фоновое задание (цикл) по требованию.
2) Фоновая задача уведомляет интерфейс основного потока на каждую итерацию.
3) В очереди выполняется только одна (1) блочная операция.
Модус Операндус
1) Используйте NSBlockOperation, чтобы содержать фоновый код.
2) Используйте региональную BOOL для переключения цикла; через IBAction.
Проблемы
1) Компилятор помечает BOOL isRunning как сильную ссылку:
Сильный захват "себя" в этом блоке, вероятно, приведет к сохранению цикла.
2) Я проверил, есть ли какие-либо операции в очереди, прежде чем пытаться добавить операцию блокировки.
Но я всегда получаю следующую ошибку:
- [NSOperationQueue addOperation:]: операция завершена и не может быть поставлена в очередь
Это доказательство концепции, кажется, работает, помимо заявленных проблем.
Вопрос:
1) Почему компиляция помечает BOOL как "работающую" как сильную линию, когда она просто скалер?
2) Почему я не могу повторно использовать NSOperationQueue путем добавления другого NSBlockOperation, если ни один не найден в очереди?
Ниже приведен весь код:
#define START 0
#define STOP 1
@interface ricViewController ()
@property (assign) BOOL running;
@end
@implementation ricViewController {
NSOperationQueue *operationQueue;
NSBlockOperation *blockOperation;
void (^backgroundBlock)(void);
}
@synthesize running = isRunning;
#pragma mark - ViewController methods
- (void)viewDidLoad {
operationQueue = [NSOperationQueue new];
[operationQueue setMaxConcurrentOperationCount:1];
[operationQueue setName:@"RicQueue"];
[self buildBackgroundBlock];
blockOperation = [NSBlockOperation blockOperationWithBlock:backgroundBlock];
[super viewDidLoad];
}
// -------------------------------------------------------------------------------------------------------------------
- (void)didReceiveMemoryWarning {
operationQueue = nil;
}
// -------------------------------------------------------------------------------------------------------------------
#pragma mark - Local methods
- (void)buildBackgroundBlock {
static int k = 0;
backgroundBlock = ^{
while (isRunning) { // 1) *** compiler warning flag: strong link warning ***
sleep(1);
if ([NSThread isMainThread]) {
NSLog(@"{backgroundBlock} *** Main Thread *** ***");
} else {
NSString *myString = [NSString stringWithFormat:@"{backgroundBlock} count = %i", k++];
NSLog(myString);
dispatch_async(dispatch_get_main_queue(), ^{
self.dataLabel.text = myString;
});
}
}
};
}
// -------------------------------------------------------------------------------------------------------------------
#pragma - Action methods
- (IBAction)exitAction:(UIButton *)sender {
exit(0);
}
// -------------------------------------------------------------------------------------------------------------------
- (IBAction)segmentedAction:(UISegmentedControl *)sender {
switch (sender.selectedSegmentIndex) {
case START:
NSLog(@"START");
self.running = YES;
if (operationQueue.operationCount < 1) {
[operationQueue addOperation:blockOperation]; // 2) *** fatal error on 2nd pass.
}
break;
case STOP:
NSLog(@"STOP");
self.running = NO;
break;
}
return;
}
@end
Консольный вывод:
BackgroundTask[3759:c07] STOP
BackgroundTask[3759:c07] START
BackgroundTask[3759:1303] {backgroundBlock} count = 0
BackgroundTask[3759:1303] {backgroundBlock} count = 1
BackgroundTask[3759:1303] {backgroundBlock} count = 2
BackgroundTask[3759:1303] {backgroundBlock} count = 3
BackgroundTask[3759:1303] {backgroundBlock} count = 4
BackgroundTask[3759:1303] {backgroundBlock} count = 5
BackgroundTask[3759:1303] {backgroundBlock} count = 6
BackgroundTask[3759:1303] {backgroundBlock} count = 7
BackgroundTask[3759:c07] STOP
BackgroundTask[3759:1303] {backgroundBlock} count = 8
1 ответ
Я провел дальнейшие исследования и обнаружил, что
* Я должен заново создать объект NSBlockOperation для каждого 'addOperation' *, так как NSOperationQueue отказывается повторно ставить в очередь объект SAME NSOperation.
Отсюда и следующее решение:
- (IBAction)segmentedAction:(UISegmentedControl *)sender {
switch (sender.selectedSegmentIndex) {
case START:
NSLog(@"START");
self.running = YES;
blockOperation = nil;
[self buildBackgroundBlock];
blockOperation = [NSBlockOperation blockOperationWithBlock:backgroundBlock];
[operationQueue addOperation:blockOperation];
break;
case STOP:
NSLog(@"STOP");
self.running = NO;
break;
}
}
... что касается компилятора, связывает BOOL isRunning со статическим self:
Сильный захват "себя" в этом блоке может привести к циклу переобучения.
Все ссылки на себя должны быть через слабую ссылку.
- (void)buildBackgroundBlock {
static int k = 0;
BOOL myRunning = isRunning;
__weak ricViewController *weakObject = self;
backgroundBlock = ^{
while (myRunning) {
sleep(1);
if ([NSThread isMainThread]) {
NSLog(@"{backgroundBlock} *** Main Thread *** ***");
} else {
NSString *myString = [NSString stringWithFormat:@"{backgroundBlock} count = %i", k++];
NSLog(myString);
dispatch_async(dispatch_get_main_queue(), ^{
weakObject.dataLabel.text = [myString copy];
});
}
}
};
}