问题
I couldn't find an answer anywhere else on the net so any help would be appreciated.
I am tying to create a system whereby I can retrieve the results of an NSOperation task, which I understand cannot be done by concrete subclasses such as NSInvocation.
I have an NSOperation subclass (TheEngine) which is abstract by convention and must be extended to implement the function -main
, to include the body of code to execute.
TheEngine contains the following initialisation function whose job is simply to note theSelector
and theObject
the selector belongs to. It also registers a KV observer for the property isFinished
:
-(id)initWithCallbackSelector:(SEL)theSelector inObject:(id)theObject
In my observeValueForKeyPath:ofObject:change:context:
function I would like to call the callback function like so:
NSLog(@"Some debug text to ensure this function is being called", nil);
[theObject performSelector:theSelector withObject:someData afterDelay:0];
The whole process goes like this:
aViewController fires up an extension of TheEngine - lets say TheTask by calling the following and adding it to an operations queue.
TheTask* TT = [[TheTask alloc] initWithCallbackSelector:
@selector(resultHandler:) inObject:theObject];
Everything seems to run as expected without any errors or exceptions at all. But when execution reaches the observeValueForKeyPath:ofObject:change:context:
the callback is not actually called. I'm new to Obj-C, so I'm not entirely sure if my understanding of this type of threading is correct.
Here is the entire code:
-(id)initWithCallbackSelector:(SEL)theSelector inObject:(id)theObject{
if([self init]){
self.selectorsParentObject = theObject;
self.selectorToCallWhenFinished = theSelector;
[self addObserver:self forKeyPath:@"isFinished" options:NSKeyValueObservingOptionNew context:NULL];
return self;
}
return nil;
}
-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)theObject change:(NSDictionary*)theChange context:(void*)theContext{
if([keyPath isEqualToString:@"isFinished"]){
NSLog(@"activity is finished with change: %@", theChange);
NSLog(@"target object: %@", self.selectorsParentObject);
NSLog(@"target selector: %@", NSStringFromSelector(self.selectorToCallWhenFinished));
//[self performSelectorOnMainThread:self.selectorToCallWhenFinished withObject:self.resultData waitUntilDone:NO];
[self.selectorsParentObject performSelector:@selector(selectorToCallWhenFinished) withObject:self.resultData afterDelay:0];
}
}
Any help appreciated!
回答1:
Your NSOperation
is likely running on a background thread. If that thread goes away, or if that thread fails to pump its run loop, then your call to performSelector:withObject:afterDelay:
will not fire. You commented out a call to performSelectorOnMainThread:...
. Did this work?
You probably should be running this on the main thread or running this with performSelector:withObject:
(without the afterDelay:
). performSelector:withObject:
does not require a run loop.
回答2:
As Rob suggested, the code was running in a background thread, as was the call observeValueForKeyPath:ofObject:change:context:
I had initially changed the code so that the selector was fired on the main thread with [self performSelectorOnMainThread:@selector(runCallback) withObject:nil waitUntilDone:NO];
.
But in this case the main thread turns out to be the TheTask and an exception was thrown as TheTask does not own theSelector
. To correct this, I created an extra function -runCallback
and fired it from
-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)theObject change:(NSDictionary*)theChange context:(void*)theContext{
if([keyPath isEqualToString:@"isFinished"]){
NSLog(@"activity is finished with change: %@", theChange);
NSLog(@"target object: %@", self.selectorsParentObject);
NSLog(@"target selector: %@", NSStringFromSelector(self.selectorToCallWhenFinished));
[self performSelectorOnMainThread:@selector(runCallback) withObject:nil waitUntilDone:NO];
//[self performSelectorOnMainThread:self.selectorToCallWhenFinished withObject:self.resultData waitUntilDone:NO];
//[self.selectorsParentObject performSelector:@selector(selectorToCallWhenFinished) withObject:self.resultData afterDelay:0];
}
}
and in the -runCallback
:
-(void)runCallback{
[self.selectorsParentObject performSelector:self.selectorToCallWhenFinished withObject:self.resultData afterDelay:0];
}
This called theSelector
in TheTask with the correct data.
Thanks for participating :)
来源:https://stackoverflow.com/questions/7277839/performselector-in-nsoperation-subclass