ios - NSOperationQueue 的 addOperation : operation is finished and cannot be enqueued?

标签 ios objective-c nsoperationqueue cloudkit nsblockoperation

注意:
这只是一个概念验证。
真正的后台任务将是不断请求原始数据的“HTTP Get”并通过主线程显示它;按需。

场景:
1)按需切换后台任务(循环)。
2) 后台任务每次迭代通知主线程UI。
3) 只有一 (1) 个 block 操作在队列中运行。

作案手法
1) 使用NSBlockOperation包含后台代码。
2) 使用区域 BOOL 来切换循环;通过 IBAction。

问题
1) 编译器将 BOOL 'isRunning' 标记为 strong 链接:

Capturing 'self' strongly in this block is likely to lead to a retain cycle.


2)在尝试添加 block 操作之前,我检查了队列中是否有任何操作。
但我总是得到以下错误:

-[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued

除了上述问题之外,该概念验证似乎还有效。

问题:
1)当它只是一个缩放器时,为什么编译将 BOOL 'running' 标记为强行?
2)如果在队列中找不到任何东西,为什么我不能通过添加另一个 NSBlockOperation 来重用 NSOperationQueue?

以下是完整代码:

#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

最佳答案

我做了进一步的研究,发现
* 我必须重新创建每个“addOperation”的 NSBlockOperation 对象 *,因为 NSOperationQueue 拒绝 重新排队相同的 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”相关联:

Capturing 'self' strongly in this block is likely to lead to a retrain cycle.

所有对“ 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];
                });
            }
        }
    };
}

关于ios - NSOperationQueue 的 addOperation : operation is finished and cannot be enqueued?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16844806/

相关文章:

iphone - 使用自定义部分高度时,UITableView 在弹跳后不会返回到正确的位置

iphone - UISegmentedControl 值以编程方式更改

ios - 快速将图像从设备加载到 tableView

ios - 在后台 NSOperationQueue 上执行文件操作是否安全?

android - 从 Firemonkey Multi Device Delphi 项目中的函数获取模态结果

ios - 没有@3x 图像和某些图像为 JPG 时的应用程序细化

ios - 如何将类对象转换为 NSDictionary?

ios - NSOperationQueue : cancel an operation after a timeout given

ios - 以编程方式添加自定义按钮相互混淆

php - 验证 appReceiptStoreURL 返回 21002 状态