问题
I'm using following function to get my application notified after the operation in nsoperationqueue has finished, so that I can schedule the task that's dependent upon the result of the operation. I'm using:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if([keyPath isEqual:@"isFinished"] && _operation == object)
{
NSLog(@"Our Thread Finished!");
[_operation removeObserver:self forKeyPath:@"isFinished"];
[self performSelectorOnMainThread:@selector(showDialog) withObject:nil waitUntilDone:YES];
}
}
My question is since mostly the tasks assigned to these operations are parsing of data if I try to tap some other button or basically do something that results in action, I get the following exception:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<Settings: 0x21b970>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: isFinished
I perfectly understand that since I try doing other things on main thread, because of which the call to main thread:
[self performSelectorOnMainThread:@selector(showDialog) withObject:nil waitUntilDone:YES];
fails to get executed. But what's the solution to this problem as I want both allow user do any action after making a request and also perform the action scheduled after finishing the task assigned to the operation.
Is it really possible?
Thanx in advance.
回答1:
If you can require Mac OS X 10.6 Snow Leopard, or (I think) iPhone OS 3.0, you can avoid KVO entirely for this. Just create another operation for the work you want to do on the main thread, add the operation it needs to follow as a dependency, and then put the main-thread operation on the main queue:
NSBlockOperration *mainThreadOp = [NSBlockOperation blockOperationWithBlock:^{
[self showDialog];
}];
[mainThreadOp addDependency:backgroundOp];
[[NSOperationQueue mainQueue] addOperation:mainThreadOp];
NSOperation supports dependencies between operations on different queues. Just be careful not to make operations on different queues mutually dependent, because that will result in deadlock.
回答2:
The reason your are seeing this problem is that you are not using the context
pointer.
When you add or remove an observer, pass a context pointer. In your observeValueForKeyPath:ofObject:change:context:
, check the context point to make sure the observation being passed belongs to you. If not, call super. It's possible to use self as the context point, or you can take the address of a static string, etc.
Here is an example:
Adding the observer:
[sample addObserver:self forKeyPath:@"finished" options:[self observationOptions] context:(void *)self];
Handling the change notification:
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == (__bridge void *)self){
if ([keyPath isEqualToString:@"finished"]){
// Handle the change here.
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
Removing the observer:
[sample removeObserver:self forKeyPath:@"finished" context:(void *)self];
Because your code was not able to pass one of the notifications to super in observeValueForKeyPath:ofObject:change:context:
, that notification went in but never came out. This is why you got that exception.
It unfortunate that many of the Key-Value Observing examples available on The IntarWebs do not do this correctly and/or pass NULL
as the context pointer (even Apple's documentation does this).
来源:https://stackoverflow.com/questions/3202491/nsoperation-kvo-problem