2013-02-08 78 views
2

我有一個NSOperationQueue處理從循環中的Web服務器導入數據。它通過以下設計實現了這一點。停止NSOperationQueue

  1. NSURLConnect被包裝在一個的NSOperation並加入到隊列

  2. 在下載的成功完成(使用的塊),從請求中的數據被封裝在另一個的NSOperation,增加了相關的數據核心數據。該操作被添加到隊列中。

  3. 成功完成(使用另一個塊),(並在指定的延遲之後),我調用啓動它的方法並返回到步驟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]; 

    }; 
} 
+0

你最初的方法應該可行,但要說明爲什麼你的取消不起作用,我將不得不看到更多的代碼(你的下載'NSOperation'是如何工作的,你如何添加其他操作?)。如果您從多個線程訪問您的可變字典,您的最終解決方案也可能會有問題。 – Sven 2013-02-08 22:34:29

+0

我會發布一些額外的代碼。我不想在不同線程上使用可變字典。我希望我不會有NSMutableDictionary的線程安全問題。我可以在主線程上設置字典的值,並且每個連接都在主線程上創建,然後被添加到隊列中以在後臺操作,但是我承認我並不完全確定自己,因爲我相對知道到多線程編碼。 – hatunike 2013-02-08 23:02:10

回答

3

NSOperation的取消只是一個請求,一個設置在NSOperation中的標誌。這取決於你的NSOperation子類是否實際行動該請求並取消它的工作。然後您需要確保您爲isExecutingisFinished等設置了正確的標誌。您還需要以符合KVO的方式執行此操作。只有設置了這些標誌,操作才完成。

在文檔Concurrency Programming Guide -> Configuring Operations for Concurrent Execution中有一個示例。雖然我明白這個例子可能沒有正確解釋所有的多線程邊界情況。示例代碼LinkedImageFetcher提供了另一個更復雜的示例:QRunLoopOperation

如果您認爲自己正確響應取消請求,那麼您確實需要發佈NSOperation子類代碼以進一步檢查問題。

+0

也許你可以澄清我對這個問題有點困惑。這是我的理解,你只需要重寫這些方法並處理標誌,如果你是專門聲明你的NSOperation子類爲「併發」操作。做出這種區分有什麼好處?隊列不處理將操作放在不同的線程上嗎?最後,關於手頭的問題。我很確定我缺乏實現這些功能的原因是我的沖洗方法無法正常工作。假設,我正確實施這些原始的沖洗方法是有效的和好的? – hatunike 2013-02-14 00:27:06

+0

我假設你正在異步使用'NSURLConnection'。如果是這樣,這將使你的'NSOperation'併發。如果你有一個固有的併發操作,但沒有遵循管理它的指導原則,那麼這可能是你問題的根源。或者,您可能正在使用'sendSynchronousRequest',在這種情況下,我不知道問題的原因是什麼,但我不確定這是使用NSURLConnection的最佳方式,雖然它可能適用於您的情況。 – 2013-02-14 10:17:14

2

而不是使用自己的標誌,如果是OK,以增加更多的操作,你可以嘗試在NSOperationQueue

- (void)setSuspended:(BOOL)suspend

方法?在添加新操作之前,請檢查隊列是否被isSuspended掛起?

+0

我做了這些更改。任何地方我添加一個操作,我檢查,看看是否暫停。現在我的flushQueue看起來像這樣:[self.operationQueue setSuspended:YES]; [self.operationQueue cancelAllOperations]; [self.operationQueue waitUntilAllOperationsAreFinished]; [self.operationQueue setSuspended:NO]; 但不幸的是這有相同的結果。 – hatunike 2013-02-08 21:08:48

+0

好吧,這可能是一個長鏡頭。我看到你至少找到了某種解決方法=) – 2013-02-08 22:34:02