我有一個NSOperationQueue處理從循環中的Web服務器導入數據。它通過以下設計實現了這一點。停止NSOperationQueue
NSURLConnect被包裝在一個的NSOperation並加入到隊列
在下載的成功完成(使用的塊),從請求中的數據被封裝在另一個的NSOperation,增加了相關的數據核心數據。該操作被添加到隊列中。
成功完成(使用另一個塊),(並在指定的延遲之後),我調用啓動它的方法並返回到步驟1。因此,我在x秒後再調用另一個服務器。
This works great。我能夠從服務器獲取數據並處理背景中的所有內容。由於這些只是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。
這個解決方案有幾個缺點(必須手動管理一個額外的數據結構,並且每一個操作都必須重新開始,以確定它在結束之前是否打開或關閉)。
編輯 - 在追求更準確的解決方案
我去掉了所有的代碼是不相關的(注意沒有錯誤處理)。我發佈了兩種方法。第一個是創建請求NSOperation的示例,第二個是生成完成塊的便捷方法。
請注意,完成塊生成器由幾十個與第一個方法類似的不同請求調用。
- (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];
};
}
你最初的方法應該可行,但要說明爲什麼你的取消不起作用,我將不得不看到更多的代碼(你的下載'NSOperation'是如何工作的,你如何添加其他操作?)。如果您從多個線程訪問您的可變字典,您的最終解決方案也可能會有問題。 – Sven 2013-02-08 22:34:29
我會發布一些額外的代碼。我不想在不同線程上使用可變字典。我希望我不會有NSMutableDictionary的線程安全問題。我可以在主線程上設置字典的值,並且每個連接都在主線程上創建,然後被添加到隊列中以在後臺操作,但是我承認我並不完全確定自己,因爲我相對知道到多線程編碼。 – hatunike 2013-02-08 23:02:10