为什么我的completionBlock永远不会在一个名为的NSOperation?(Why my

2019-08-18 00:30发布

我sublcassed一个的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];
...
}

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

然后,在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子类的使用为完成块呢?

任何意见或指向一个类似的解决情况,将不胜感激。

Answer 1:

你有种陷入并发和非并发之间一个奇怪的空间NSOperation这里的子类。 通常,当您实现main ,你的操作是不同步,并isFinished变化YES ,一旦main出口。

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

快速解决您的问题,是落实setFinished:使用(will|did)ChangeValueForKey:来电。 (我也改变了伊娃的名字来命名反映当时的命名约定)。 下面是一个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


Answer 2:

而实现的异步子我遇到了这个错误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" ,分别,我们需要抛出KVO通知"isExecuting""isFinished" 。 这就是为什么completionBlock是没有得到调用。

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

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

self.willChangeValue(forKey: "isFinished")
self._finished = true
self.didChangeValue(forKey: "isFinished")


文章来源: Why my completionBlock never gets called in an NSOperation?