2

ループ上の Web サーバーからのデータのインポートを処理する NSOperationQueue があります。これは、次の設計で実現されます。

  1. NSURLConnect は NSOperation にラップされ、キューに追加されます

  2. (ブロックを使用して) ダウンロードが正常に完了すると、要求からのデータは、関連データをコア データに追加する別の NSOperation にラップされます。この操作はキューに追加されます。

  3. (別のブロックを使用して) 正常に完了すると (指定された遅延の後)、すべてを開始したメソッドを呼び出し、ステップ 1 に戻ります。したがって、x 秒後に別のサーバー呼び出しを行います。

これはうまくいきます。サーバーからデータを取得し、バックグラウンドですべてを処理できます。これらは単なる NSOperations であるため、すべてをバックグラウンドに置いて、一度に複数の要求を実行できます。これは本当にうまくいきます。

私が現在抱えている唯一の問題は、操作を行った後、操作を正常にキャンセルできないことです。

私は次のようなものを試しました:

- (void)flushQueue
{
    self.isFlushingQueue = YES;
    [self.operationQueue cancelAllOperations];
    [self.operationQueue waitUntilAllOperationsAreFinished];
    self.isFlushingQueue = NO;
    NSLog(@"successfully flushed Queue");

}

ここで、self.isFlushingQueue は、新しい操作をキューに追加する前に確認するために使用する BOOL です。これは機能するはずですが、実際には機能しません。私のフランケンシュタインの創造を止めるためのアイデアはありますか?

編集(問題は解決しましたが、別の観点から)

これらの操作をキャンセルできなかった理由についてはまだ困惑しています (可能な解決策を引き続き試していただければ幸いです) が、この問題を少し異なる方法で解決する方法についての洞察が得られました。キャンセル操作をまったく処理せず、キューが終了するまで待機する代わりに、すべてのアクティブな接続のリストを持つデータ構造 (NSMutableDictionary) を持つことにしました。このようなもの :

self.activeConnections = [NSMutableDictionary dictionaryWithDictionary:@{
                          @"UpdateContacts": @YES,
                          @"UpdateGroups" : @YES}];

そして、操作をキューに追加する前に、その特定の呼び出しがオンかオフかを尋ねるだけです。私はこれをテストしましたが、ループさせたい個々のサーバー要求を有限に制御することに成功しました。すべてをオフにするには、すべての接続を @NO に設定します。

このソリューションにはいくつかの欠点があります (追加のデータ構造を手動で管理する必要があり、終了する前にすべての操作を再開してオンかオフかを確認する必要があります)。

編集 -- より正確な解決策を求めて

関係のないコードはすべて削除しました (エラー処理がないことに注意してください)。2つの方法を投稿しました。1 つ目はリクエスト NSOperation の作成方法の例で、2 つ目は完了ブロックを生成するための便利なメソッドです。

完了ブロック ジェネレーターは、最初のメソッドと同様に、数十の異なる要求によって呼び出されることに注意してください。

- (void)updateContactsWithOptions:(NSDictionary*)options
{
    //Hard coded for ease of understanding
    NSString *contactsURL = @"api/url";
    NSDictionary *params = @{@"sortBy" : @"LastName"};

    NSMutableURLRequest *request = [self createRequestUsingURLString:contactsURL andParameters:params];

    ConnectionCompleteBlock processBlock = [self blockForImportingDataToEntity:@"Contact"
                                                                 usingSelector:@selector(updateContactsWithOptions:)
                                                                   withOptions:options andParsingSelector:@selector(requestUsesRowsFromData:)];

    BBYConnectionOperation *op = [[BBYConnectionOperation alloc] initWithURLRequest:request
                                                                        andDelegate:self
                                                                 andCompletionBlock:processBlock];

    //This used to check using self.isFlushingQueue
    if ([[self.activeConnections objectForKey:@"UpdateContacts"] isEqualToNumber:@YES]){
        [self.operationQueue addOperation:op];
    }

}

- (ConnectionCompleteBlock) blockForImportingDataToEntity:(NSString*)entityName usingSelector:(SEL)loopSelector withOptions:(NSDictionary*)options andParsingSelector:(SEL)parseSelector
{
    return ^(BOOL success, NSData *connectionData, NSError *error){

        //Pull out variables from options
        BOOL doesLoop = [[options valueForKey:@"doesLoop"] boolValue];
        NSTimeInterval timeInterval = [[options valueForKey:@"interval"] integerValue];

        //Data processed before importing to core data
        NSData *dataToImport = [self performSelector:parseSelector withObject:connectionData];

        BBYImportToCoreDataOperation *importOperation = [[BBYImportToCoreDataOperation alloc] initWithData:dataToImport 
          andContext:self.managedObjectContext 
          andNameOfEntityToImport:entityName];

        [importOperation setCompletionBlock:^ (BOOL success, NSError *error){
             if(success){
                 NSLog(@"Import %@s was successful",entityName);
                 if(doesLoop == YES){
                     dispatch_async(dispatch_get_main_queue(), ^{
                         [self performSelector:loopSelector withObject:options afterDelay:timeInterval];
                     });
                 }
             }
         }];

        [self.operationQueue addOperation:importOperation];

    };
}
4

2 に答える 2

3

NSOperation のキャンセルは単なる要求であり、 で設定されるフラグですNSOperationNSOperation実際に作業を要求してキャンセルするアクションは、サブクラス次第です。isExecuting次に、 andなどに正しいフラグを設定したことを確認する必要がありますisFinished。また、KVO に準拠した方法でこれを行う必要があります。これらのフラグが設定されると、操作は終了します。

ドキュメントConcurrency Programming Guide -> Configuring Operations for Concurrent Executionに例があります。この例がすべてのマルチスレッド エッジ ケースを正しく説明していない可能性があることは理解していますが。サンプル コードには、別のより複雑な例が示されていますLinkedImageFetcherQRunLoopOperation

キャンセル要求に正しく応答していると思われる場合は、NSOperation サブクラス コードを投稿して問題をさらに調べる必要があります。

于 2013-02-09T15:39:02.467 に答える
2

さらに操作を追加してもよい場合に独自のフラグを使用する代わりに、

- (void)setSuspended:(BOOL)suspend

方法NSOperationQueue?そして、新しい操作を追加する前に、キューが中断されているかどうかをisSuspended?で確認してください。

于 2013-02-08T20:47:51.850 に答える