ループ上の Web サーバーからのデータのインポートを処理する NSOperationQueue があります。これは、次の設計で実現されます。
NSURLConnect は NSOperation にラップされ、キューに追加されます
(ブロックを使用して) ダウンロードが正常に完了すると、要求からのデータは、関連データをコア データに追加する別の NSOperation にラップされます。この操作はキューに追加されます。
(別のブロックを使用して) 正常に完了すると (指定された遅延の後)、すべてを開始したメソッドを呼び出し、ステップ 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];
};
}