[英]Stopping an NSOperationQueue
我有一个NSOperationQueue,它可以循环处理从Web服务器导入的数据。 它通过以下设计来实现。
NSURLConnect包装在NSOperation中并添加到队列中
成功完成下载(使用块)后,来自请求的数据将包装在另一个NSOperation中,该NSOperation将相关数据添加到Core Data。 此操作已添加到队列中。
在成功完成(使用另一个块)之后(在指定的延迟之后),我调用启动所有方法的方法,然后返回到步骤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];
};
}
取消NSOperation只是一个请求,这是在NSOperation
设置的NSOperation
。 由NSOperation
子类实际操作该请求并取消它的工作。 然后,您需要确保已为isExecuting
和isFinished
等设置了正确的标志。您还需要以符合KVO的方式执行此操作。 只有设置了这些标志后,操作才能完成。
文档并发编程指南->配置并发执行的操作中有一个示例。 尽管我知道此示例可能无法正确解决所有多线程边缘情况。 示例代码LinkedImageFetcher
提供了另一个更复杂的示例: QRunLoopOperation
如果您认为自己正确地响应了取消请求,那么您确实需要发布NSOperation子类代码以进一步检查问题。
您可以尝试尝试以下操作,而不是在可以添加更多操作时使用自己的标志
- (void)setSuspended:(BOOL)suspend
NSOperationQueue
方法? 在添加新操作之前,请检查队列是否被isSuspended
?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.