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];
                });
            }
        }
    };
}
Другие вопросы по тегам