2013-03-26 136 views
10

我已经sublcassed一个NSOperation并设置我的completionBlock,但它似乎永远不会进入,即使当操作完成。这里是我的代码:为什么我的completionBlock永远不会在NSOperation中调用?

目录控制器类树立的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]; 
... 
} 

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

然后,在main我的子类NSOperation类:ImportWordOperation.m,我启动后台操作。我甚至为了推翻isFinished伊娃才会触发完成方法:

- (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结束。

我读过的代码使用了它自己的完成块,但是NSOperation子类中的完成块有什么用处?

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

回答

17

你已经陷入了一个奇怪的空间,在这里并发和非并发NSOperation子类。通常,当您执行main时,您的操作是非并发的,并且main退出时isFinished更改为YES

但是,您提供自己实现isFinished,而且编码它,以便isFinished不会返回YES后才main已退出。这使得您的操作在很多方面开始像一个并发操作 - 至少包括需要手动发出KVO通知。

快速解决您的问题是使用(will|did)ChangeValueForKey:调用实现setFinished:。 (我也改变了伊娃的名字以反映命名惯例的命名)。下面是一个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 

我不熟悉你的要求,因此,或许你有迫切需要,但你的操作会显得更自然适合使用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 
+0

感谢!这是'main' /'start'方法。我需要实现'start'而不是'main',因为我需要它是并发的。我改变了这一点,并调整了setFinished KVO,它的工作!我正在添加'-cancel'方法来整理事情。 – 2013-03-26 17:08:05

1

我在执行异步子类NSOperation时遇到此错误。

引用关键路径的雨燕方法是使用#keyPath指令,所以我这样做(_executing_finished是我的内部变量):

self.willChangeValue(forKey: #keyPath(Operation.isExecuting)) 
self._executing = false 
self.didChangeValue(forKey: #keyPath(Operation.isExecuting)) 

self.willChangeValue(forKey: #keyPath(Operation.isFinished)) 
self._finished = true 
self.didChangeValue(forKey: #keyPath(Operation.isFinished)) 

不幸的是,#keyPath表达上述决心"executing""finished" ,我们需要为"isExecuting""isFinished"投出KVO通知。这就是为什么completionBlock没有被调用。

的解决方案是硬编码它们的方式:

self.willChangeValue(forKey: "isExecuting") 
self._executing = false 
self.didChangeValue(forKey: "isExecuting") 

self.willChangeValue(forKey: "isFinished") 
self._finished = true 
self.didChangeValue(forKey: "isFinished") 
相关问题