11

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完了メソッドをトリガーするために、iVarをオーバーライドしました。

- (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サブクラスの完了ブロックの用途は何ですか?

同様の解決された状況へのアイデアやポイントをいただければ幸いです。

4

2 に答える 2

19

ここでは、並行サブクラスと非並行サブクラスの間の奇妙なスペースに陥っていNSOperationます。通常、を実装するmainと、操作は非並行であり、終了するとすぐににisFinished変更されます。YESmain

ただし、の独自の実装を提供し、終了するまで戻らないisFinishedようにコーディングしました。これにより、操作は多くの点で並行操作のように動作し始めます。少なくとも、手動でKVO通知を発行する必要があります。isFinishedYESmain

問題の迅速な解決策は、呼び出し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

私はあなたの要件に精通していないので、おそらくあなたは差し迫ったニーズを持っていますが、あなたの操作は、の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
于 2013-03-26T06:30:12.800 に答える
1

の非同期サブクラスの実装中にこのエラーが発生しましたNSOperation

キーパスを参照するSwiftの方法は#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解決されるため、とのKVO通知をスローする必要があります。これが、が呼び出されない理由です。"executing""finished""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")
于 2017-06-21T07:12:55.077 に答える