我有一个findBusinessOperationQueue
,为了知道剩余多少nsoperation
,我根据operations
添加了@Oserver:
- (id)init {
if (self = [super init]) {
self.findBusinessOperationQueue = [NSOperationQueue new];
[self.getBusinessOperationQueue addObserver:self
forKeyPath:@"operations"
options:0
context:nil];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([object isEqual:self.findBusinessOperationQueue] && [keyPath isEqualToString:@"operations"])
NSLog(@" ======> FindBusinessoperationQueue size: %lu", (unsigned long)[[self.findBusinessOperationQueue operations] count]);
else
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
将nsoperations
添加到其中后:
FindBusinessOperation *op = [[FindBusinessOperation alloc] init];
[self.findBusinessOperationQueue addOperation:op];
我在控制台上得到了我的期望:
======> FindBusinessOperationQueue size: 1
在FindBusinessOperation,我使用NSURLSessionDataTask
从服务器下载一些数据(在这种情况下,我下载了pdf文件。
APPROACH 1 :使用不带完成块的NSURLSessionDataTask
-(void)start {
// Dont do any downloading if op is cancelled
if (self.cancelled)
return;
NSLog(@" FindBusiness START :main thread = %d",[NSThread isMainThread]);
// Change to isExecuting status
[self willChangeValueForKey:NSLocalizedString(@"isExecuting", nil)];
opExecuting = YES;
[self didChangeValueForKey:NSLocalizedString(@"isExecuting", nil)];
NSURLSessionDataTask *task = [self.session dataTaskWithURL: [NSURL URLWithString:@"http://cdn.oreillystatic.com/oreilly/booksamplers/9781449359348_sampler.pdf"]];
[task resume];
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
completionHandler(NSURLSessionResponseAllow);
downloadSize=[response expectedContentLength];
dataToDownload=[[NSMutableData alloc]init];
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[dataToDownload appendData:data];
CGFloat dataDownloaded = [dataToDownload length ]/downloadSize ;
NSLog(@"----> is %f",dataDownloaded);
if ( (int)dataDownloaded == 1 )
[self opCompleted];
}
-(BOOL)isConcurrent {
return YES;
}
- (BOOL)isOpExecuting {
return opExecuting;
}
- (BOOL)isFinished {
return opCompleted;
}
-(void)opCompleted {
[self willChangeValueForKey:NSLocalizedString(@"isExecuting", nil)];
[self willChangeValueForKey:NSLocalizedString(@"isFinished", nil)];
opExecuting = NO;
opCompleted = YES;
[self didChangeValueForKey:NSLocalizedString(@"isExecuting", nil)];
[self didChangeValueForKey:NSLocalizedString(@"isFinished", nil)];
}
正如我们所见,当我们完成下载所有数据时会调用opCompleted
,它也会通知(BOOL)isFisnied
。从理论上讲,这个nsoperation
将从findBusinessOperationQueue
中移除,我们预计队列中的剩余操作为0.
不幸的是,observeValueForKeyPath
在进程结束时没有被调用。现在还在苦苦挣扎。
APPROACH 2 使用NSURLSessionDataTask
和completionBlock,如下所示
NSURLSessionDataTask *task = [self.session dataTaskWithURL:[NSURL URLWithString:@"http://cdn.oreillystatic.com/oreilly/booksamplers/9781449359348_sampler.pdf"]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
self.statusCode = httpResponse.statusCode;
if (self.statusCode == 200)
NSLog(@"DONE ");
else
NSLog(@"FAILED ");
NSLog(@"FINDBUSINESS DONE. S:%.2f MB", (float)data.length/1024.0f/1024.0f);
[self opCompleted];
self.resultBlock([summary.business allObjects]);
}
}
];
[task resume];
然后它起作用
2014-10-30 13:53:35.544 Discovery[1994:485268] ======> FindBusinessoperationQueue size: 1
2014-10-30 13:53:45.472 Discovery[1994:485268] FINDBUSINESS DONE. #:0 S:6.97 M
2014-10-30 13:53:50.588 Discovery[1994:485268] ======> FindBusinessoperationQueue size: 0
问题:为什么后一种方法不起作用?
如果您之前遇到此问题,请给我一个提示,欢迎所有答案。
答案 0 :(得分:0)
我知道这是一个老问题,但我会戴上帽子,看看能不能帮忙。实际上这里有很多内容,但有些内容在您的代码片段中并不清楚,因为我们实际上并没有全面了解。
首先,您没有明确说出来,但是有没有理由让您将FindBusinessOperation并发?操作通常在操作队列队列中进行,队列将始终在后台线程上执行操作。如果您正在观察操作并且您的观察者需要符合KVO,则只需要使操作并发,并向观察者报告有关其排队操作的状态更改。请参阅Configuring Operations for Concurrent Execution和KVO Compliance
其次,在您的观察课中,您是否对FindBusinessOperation操作有强烈的引用?它看起来不像你,因此当队列使你完成的操作出列时,系统可能将操作设置为nil(然后你的队列的操作数组可能会设置为nil)。当观察者被设置为零时,我认为KVO不会发送通知。
您也可以考虑使用keyPath @“operationCount”而不是@“operations”注册您的观察者,因为您的设置中的多对多关系可能存在问题。见To-Many Relationships
您也可以在改变利用NSOperation's setCompletionBlock: method的方法方面取得成功。在完成块中,您可以检查队列的operationCount - 您可能需要考虑同步以及保留指向队列实例的指针的块。 您也可以使用此完成块来触发操作的最终KVO通知。
如果没有完整的资料来源,我恐怕无法确切地说明为什么第一种方法失败而第二种方法没有。