为什么我的completionBlock永远不会在NSOperation中被调用? [英] Why my completionBlock never gets called in an NSOperation?

查看:139
本文介绍了为什么我的completionBlock永远不会在NSOperation中被调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经放弃了NSOperation并设置了我的completionBlock,但即使操作完成,它似乎永远不会进入。这是我的代码:

I've sublcassed an NSOperation and set my completionBlock but it seems to never enter even when the operation finishes. Here's my code:

目录控制器类设置NSOperation:

A catalog controller class sets up the NSOperation:

- (void)setupOperation {
...

    ImportWordOperation *importWordOperation = [[ImportWordOperation alloc] initWithCatalog:words];
    [importWordOperation setMainObjectContext:[app managedObjectContext]];
    [importWordOperation setCompletionBlock:^{
        [(ViewController *)[[app window] rootViewController] fetchResults];
    }];
    [[NSOperationQueue mainQueue] addOperation:importWordOperation];
    [importWordOperation release];
...
}

如你所见,我正在设置在一个其他控制器的主线程上执行方法的完成块。

As you can see, I'm setting the completion block to execute a method on the main thread, in some other controller.

然后,在 main 中子类NSOperation类: ImportWordOperation.m ,我启动了后台操作。我甚至覆盖了 isFinished iVar,以便触发完成方法:

Then, in main my subclassed NSOperation class: ImportWordOperation.m, I fire-up the background operation. I even overrode isFinished iVar in order for the completion method to be triggered:

- (void)setFinished:(BOOL)_finished {
    finished = _finished;
}

- (BOOL)isFinished {
    return (self.isCancelled ? YES: finished);
}

- (void)addWords:(NSDictionary *)userInfo {
    NSError *error = nil;

    AppDelegate *app = [AppDelegate sharedInstance];

    NSManagedObjectContext *localMOC = [userInfo valueForKey:@"localMOC"];
    NSEntityDescription *ent = [NSEntityDescription entityForName:@"Word" inManagedObjectContext:localMOC];
    for (NSDictionary *dictWord in [userInfo objectForKey:@"words"]) {
        Word *wordN = [[Word alloc] initWithEntity:ent insertIntoManagedObjectContext:localMOC];

        [wordN setValuesForKeysWithDictionary:dictWord];
        [wordN release];
    }

    if (![[userInfo valueForKey:@"localMOC"] save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
    [localMOC reset];

    [self setFinished:YES];
}


- (void)main {

    finished = NO;

    NSManagedObjectContext *localMOC = nil;
    NSUInteger type = NSConfinementConcurrencyType;
    localMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:type];
    [localMOC setUndoManager:nil];
    [localMOC setParentContext:[self mainObjectContext]];

    if (![self isCancelled]) {
        if ([self.words count] > 0) {
            [self performSelectorInBackground:@selector(addWords:) withObject:@{@"words":self.words, @"localMOC":localMOC}];
        }
    }
}

如果删除isFinished访问器方法,然后在 ImportWordOperation 结束之前调用完成块。

If I remove isFinished accessor methods, then the completion block gets called but way before ImportWordOperation finishes.

我读过的代码是我的发现它使用自己的完成块但是那么NSOperation子类中的完成块有什么用呢?

I've read code that I've found that uses its own completion block but then what's the use for the completion block in NSOperation subclasses anyway?

任何想法或指向类似解决的情况都将非常感激。

Any ideas or point to a similar solved situation would be greatly appreciated.

推荐答案

你有点陷入并发和非并发 NSOperation之间的奇怪空间这里的子类。通常,当您实现 main 时,您的操作是非并发的, isFinished 更改为只要 main 退出。

You've kind of fallen into a weird space between concurrent and non-concurrent NSOperation subclasses here. Typically, when you implement main, your operation is non-concurrent, and isFinished changes to YES as soon as main exits.

但是,你提供了自己的实现 isFinished ,并对其进行编码,以便 isFinished 不返回 YES 直到 main 退出。这使得您的操作在很多方面开始像并发操作一样 - 至少包括需要手动发出KVO通知。

However, you've provided your own implementation of isFinished, and also coded it so that isFinished doesn't return YES until after main has exited. This makes your operation start to act like a concurrent operation in many ways - at least including the need to manually emit KVO notifications.

问题的快速解决方案是实现 setFinished:使用(将|做)ChangeValueForKey:调用。 (我还更改了ivar名称以反映命名主流命名约定)。下面是一个 NSOperation 子类,我相信它可以准确地模拟您的操作的工作方式,以并发方式完成。

The quick solution to your problem is to implement setFinished: using (will|did)ChangeValueForKey: calls. (I also changed the ivar name to reflect naming prevailing naming conventions). Below is an NSOperation subclass that I believe accurately models your operation's workings, in terms of finishing in a concurrent fashion.

@implementation TestOperation {
    BOOL _isFinished;
}

- (void)setFinished:(BOOL)isFinished
{
    [self willChangeValueForKey:@"isFinished"];
    // Instance variable has the underscore prefix rather than the local
    _isFinished = isFinished;
    [self didChangeValueForKey:@"isFinished"];
}

- (BOOL)isFinished
{
    return ([self isCancelled] ? YES : _isFinished);
}

- (void)main
{
    NSLog(@"%@ is in main.",self);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        [self setFinished:YES];
    });
}

@end

我不熟悉你的要求所以也许你迫切需要,但你的操作似乎更适合使用 start 而不是 main的并发操作。我已经实现了一个似乎正常工作的小例子。

I'm not familiar with your requirements so perhaps you have a pressing need, but your operation would seem a more natural fit for a concurrent operation that uses start instead of main. I've implemented a small example that appears to be working correctly.

@implementation TestOperation {
    BOOL _isFinished;
    BOOL _isExecuting;
}

- (void)setFinished:(BOOL)isFinished
{
    if (isFinished != _isFinished) {
        [self willChangeValueForKey:@"isFinished"];
        // Instance variable has the underscore prefix rather than the local
        _isFinished = isFinished;
        [self didChangeValueForKey:@"isFinished"];
    }
}

- (BOOL)isFinished
{
    return _isFinished || [self isCancelled];
}

- (void)cancel
{
    [super cancel];
    if ([self isExecuting]) {
        [self setExecuting:NO];
        [self setFinished:YES];
    }
}

- (void)setExecuting:(BOOL)isExecuting {
    if (isExecuting != _isExecuting) {
        [self willChangeValueForKey:@"isExecuting"];
        _isExecuting = isExecuting;
        [self didChangeValueForKey:@"isExecuting"];
    }
}

- (BOOL)isExecuting
{
    return _isExecuting;
}

- (void)start
{
    NSLog(@"%@ is in start. isCancelled = %@", self, [self isCancelled] ? @"YES" : @"NO");
    if (![self isCancelled]) {
        [self setFinished:NO];
        [self setExecuting:YES];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
            sleep(1);
            [self setExecuting:NO];
            [self setFinished:YES];
        });
    }
}
@end

这篇关于为什么我的completionBlock永远不会在NSOperation中被调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆