2

我有一个 NSOperationQueue 处理循环中从 Web 服务器导入数据。它通过以下设计实现了这一点。

  1. NSURLConnect 封装在一个 NSOperation 中并添加到队列中

  2. 成功完成下载后(使用块),来自请求的数据被包装在另一个 NSOperation 中,该 NSOperation 将相关数据添加到 Core Data。此操作被添加到队列中。

  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。

这个解决方案有几个缺点(必须手动管理一个额外的数据结构,并且每个操作都必须重新开始以查看它在终止之前是打开还是关闭)。

编辑——追求更准确的解决方案

我删除了所有不相关的代码(注意没有错误处理)。我发布了两种方法。第一个是如何创建请求 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];

    };
}
4

2 回答 2

3

取消 NSOperation 只是一个请求,一个设置在NSOperation. NSOperation实际操作该请求并取消它的工作取决于您的子类。然后,您需要确保为等设置了正确的标志isExecutingisFinished您还需要以符合 KVO 的方式执行此操作。只有设置了这些标志后,操作才完成。

文档Concurrency Programming Guide -> Configure Operations for Concurrent Execution中有一个示例。尽管我了解此示例可能无法正确解释所有多线程边缘情况。示例代码中提供了另一个更复杂的示例LinkedImageFetcherQRunLoopOperation

If you think you are responding to the cancellation request correctly then you really need to post your NSOperation subclass code to examine the problem any further.

于 2013-02-09T15:39:02.467 回答
2

当可以添加更多操作时,不要使用自己的标志,您可以尝试

- (void)setSuspended:(BOOL)suspend

方法上NSOperationQueue?在添加新操作之前,检查队列是否被挂起isSuspended

于 2013-02-08T20:47:51.850 回答