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通知

如果没有完整的来源,恐怕我无法确切地说出为什么第一种方法失败,而第二种方法没有