Ios 操作完成时,未调用nsoperation队列上的observeValueForKeyPath
我有一个Ios 操作完成时,未调用nsoperation队列上的observeValueForKeyPath,ios,objective-c,nsoperation,nsoperationqueue,Ios,Objective C,Nsoperation,Nsoperationqueue,我有一个findBusinessOperationQueue,为了知道还有多少nsoperation,我根据操作添加了观察者: - (id)init { if (self = [super init]) { self.findBusinessOperationQueue = [NSOperationQueue new]; [self.getBusinessOperationQueue addObserver:self
findBusinessOperationQueue
,为了知道还有多少nsoperation
,我根据操作添加了观察者:
- (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文件)
方法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)isfined
。理论上,这个nsoperation
将从findBusinessOperationQueue
中删除,我们预计队列上的剩余操作为0
不幸的是,observeValueForKeyPath
在进程结束时未被调用。目前仍在努力
方法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
问:为什么后一种方法不起作用
如果您以前遇到过这个问题,请给我一个提示,这里欢迎您提供所有答案。我知道这是一个老问题,但我会尽我所能,看看我是否能提供帮助。这里实际上有很多事情,但从您的代码片段中有些事情不清楚,因为我们实际上没有完整的信息
首先,您没有明确指出这一点,但是为什么要使FindBusinessOperation并发?操作通常在操作队列上进行,队列将始终在后台线程上执行其操作。如果您正在观察操作,并且您的观察者需要e KVO兼容,向观察者报告其排队操作的状态更改。请参阅和
第二,在您的观察类中,您是否持有对FindBusinessOperation操作的强引用?看起来您不是,因此当队列将您完成的操作出列时,系统可能会将该操作设置为nil(然后,您队列的操作数组可能会设置为nil).我不认为KVO会在观察者设置为零时发送通知
您还可以考虑用KEYPATH @“操作数”代替“@操作”来注册您的观察者,因为在安装过程中可能存在许多关系问题。请参阅
你也可以在转移你的方法中找到成功。在完成块中,你可以检查队列的操作数——你可能需要考虑同步以及保留指向你的队列实例的指针的那个块。
您还可以使用此完成块触发操作的最终KVO通知
如果没有完整的来源,恐怕我无法确切地说出为什么第一种方法失败,而第二种方法没有