问题
I'm trying to parse some XML files in the background so that the UI doesn't freeze. I have to check two things:
- NSOperationQueue is finished?
- NSOperation - parsing did fail?
I have a class that subclasses NSOperation and a delegate is called if the parsing failed. Operations in the queue are limited to one simultaneously.
My problem is that I can't rely on the fact that the failed message is executed before I get the queue did finish message. Sometimes I don't get a failed message before I get the finished message. Then, for example, I have this order:
Operation 1 Successful Operation 2 Successful OperationQueue finished Operation 3 Failed
All messages are sent to the main thread. After I get the finished message I want to proceed in my code, but only if all operations were successful. How can I handle the problem that the delegate message is called after my queue is finished.
This are some parts of my code:
//XMLOperation.m
- (void)main {
NSLog(@"Operation started");
if ([self parseXML]) {
[self performSelectorOnMainThread:@selector(finishedXMLParsing) withObject:nil waitUntilDone:NO];
} else {
[self performSelectorOnMainThread:@selector(failedXMLParsing) withObject:nil waitUntilDone:NO];
}
}
NSLog(@"Operation finished");
}
//StartController.m
[self.xmlParseQueue addObserver:self forKeyPath:@"operations" options:0 context:NULL];
...
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (object == self.xmlParseQueue && [keyPath isEqualToString:@"operations"]) {
if ([self.xmlParseQueue.operations count] == 0) {
// Do something here when your queue has completed
NSLog(@"queue has completed");
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}
回答1:
This probably happens because your KVO notification for the operations
property of the queue is not necessarily delivered on the main thread while your finished/failed notifications are.
You should ensure that the completion notification is performed on the main thread as well, so that the order of your notifications is defined.
回答2:
Resume this question and paste my code solution here.
void RunInMainThread(void (^block)(void))
{
if (!block) {
return;
}
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
}
@interface MyOperationQueue : NSOperationQueue
@property (nonatomic, assign) NSUInteger totalCount;
@property (nonatomic, copy) dispatch_block_t allOperationCompletionBlock;
@end
@implementation MyOperationQueue
- (void)addOperation:(NSOperation *)op
{
[super addOperation:op];
__weak typeof(self) weakSelf = self;
self.totalCount++;
DDLogVerbose(@"Added a operation, total: %ld operation(s).", self.totalCount);
op.completionBlock = ^ {
weakSelf.totalCount--;
DDLogVerbose(@"Finished a operation, left: %ld operation(s).", weakSelf.totalCount);
if (weakSelf.totalCount == 0) {
if (weakSelf.allOperationCompletionBlock) {
RunInMainThread(weakSelf.allOperationCompletionBlock);
}
}
};
}
@end
来源:https://stackoverflow.com/questions/7805702/how-to-check-if-nsoperationqueue-is-finished-and-if-any-operation-failed