停止NSOperationQueue

时间:2013-02-08 20:35:54

标签: ios nsurlconnection nsoperation nsoperationqueue

我有一个NSOperationQueue,用于处理从循环上的Web服务器导入数据。它通过以下设计实现了这一目标。

  1. NSURLConnect包含在NSOperation中并添加到队列

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

  3. 成功完成(使用另一个块),(并在指定的延迟后),我调用启动它的方法并返回步骤1.因此,我在x秒后再次进行服务器调用。

  4. 这很有效。我能够从服务器获取数据并处理后台的所有内容。因为这些只是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];
    
        };
    }
    

2 个答案:

答案 0 :(得分:3)

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

文档Concurrency Programming Guide -> Configuring Operations for Concurrent Execution中有一个示例。虽然我理解这个例子可能无法正确解释所有多线程边缘情况。示例代码LinkedImageFetcher中提供了另一个更复杂的示例:QRunLoopOperation

如果您认为您正在正确回复取消请求,那么您确实需要发布您的NSOperation子类代码以进一步检查问题。

答案 1 :(得分:2)

可以尝试

,而不是在可以添加更多操作时使用自己的标志

- (void)setSuspended:(BOOL)suspend

NSOperationQueue上的

方法?在添加新操作之前,请检查队列是否已暂停isSuspended