为什么我的completionBlock永远不会在NSOperation中被调用?

car*_*_ms 11 iphone nsoperation ios6

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

目录控制器类设置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];
...
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我正在设置完成块以在主线程上执行某个方法,在其他控制器中执行.

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

- (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}];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我删除了isFinished访问器方法,那么在完成之前会调用完成块ImportWordOperation.

我已经阅读了我发现的使用自己的完成块的代码,但是那么NSOperation子类中的完成块有什么用呢?

任何想法或指向类似的解决情况将不胜感激.

Car*_*zey 18

你有点陷入并发和非并发NSOperation子类之间的奇怪空间.通常,在实施时main,您的操作是非并发的,并在退出时立即isFinished更改.YESmain

但是,您已经提供了自己的实现isFinished,并对其进行了编码,以便在退出之后isFinished才会返回.这使得您的操作在许多方面开始像并发操作一样 - 至少包括手动发出KVO通知的需要.YESmain

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

@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
Run Code Online (Sandbox Code Playgroud)

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

@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
Run Code Online (Sandbox Code Playgroud)