2013-03-26 135 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") 
相關問題