问题
I've created an NSOperation in the queue like so:
ImageLoadingOperation *operation = [[ImageLoadingOperation alloc] initWithImageURL:url target:self action:@selector(didFinishLoadingImageWithResult:)];
[operationQueue addOperation:operation];
[operation release];
And this works fine but if the view gets popped before the operation finishes the App crashes with "EXC_BAD_ACCESS"
I've tried to cancel the the operation Queue by calling cancelAllOperations but as its already in process it doesn't prevent the App from crashing. The docos say that if the operation is running it is up to the operation to detect that it has been canceled and respond appropriately but not too sure how I would implement this?
Any ideas?
回答1:
It is a general problem for View calling some network and then callback.
My solution is you can retain the view before you call the operation. And then, when the operation finishes, you release the view.
- (void)longTask {
[self retain];
}
- (void)longTaskDidFinish {
// do something if you want
[self release];
}
回答2:
You will have to either override the "cancel" operation in your ImageLoadingOperation class, or have your ImageLoadingOperation add itself as KVO observer to the "cancelled" property. There - you can intelligently cancel your operation in such way that it won't crash.
Also, if your ImageLoadingOperation runs in the background, it would be wiser to defer your access to the views somehow to the main thread (where all drawing takes place). You could use a dispatch_sync(dispatch_get_main_queue(), ^{}); or even performSelectorOnMainThread for actual access to the related view.
You see - the whole point of using an operation queue is to remove dependencies, and let things run in parallel, but your operation must synchronize with the view-system changes, and that must be designed for completeness and robustness.
回答3:
You could retain the view before the callback of operation is called, as vodkhang mentioned above. But that will prolong the life of the view unnecessarily because since the view is popped you don't want the operation to continue any more.
Here is a sketch about what you should do to respond to the cancel command:
- (void)start{
if(self.isCancelled){
[self markAsFinished];
return;
}
//start your task asynchronously
}
//If you want to cancel the downloading progress immediately, implement your own 'cancel' method
- (void)cancel{
[super cancel];
if(self.isExecuting){
{
......
cancel load process
......
}
[self markAsFinished];
}
}
- (void)markAsFinished{
......
change 'finished' to YES' generate KVO notifications on this key path
change 'executing' to 'YES'; generate KVO notification on this key path
......
}
This sketch is based on ASIHTTPRequest networking library, and there is an official guide on how you should respond to cancel command.
来源:https://stackoverflow.com/questions/3632472/issues-with-nsoperationqueue-and-dealloc-being-called-and-crashing-app