问题
Building on an earlier question:
I have an NSOperationQueue
that looks like this:
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
[someObject someSelector];
}];
NSBlockOperation *block2= [NSBlockOperation blockOperationWithBlock:^{
[someObject anotherSelector];
}];
[block2 addDependency:block1];
[queue addOperation:block1];
[queue addOperation:block2];
Now, inside someSelector
I have:
returnData = [requesterObj getDataWithURL:(NSString*)url];
where getDataWithURL
contains something like:
NSURL *requestUrl = [NSURL URLWithString:strUrl];
NSMutableURLRequest *request = [NSMutableURLRequest requestUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:timeout];
NSError *requestError;
NSURLResponse *urlResponse = nil;
NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
Now, when I add breakpoints, it appears that the second block is being called before the NSURLConnection
from the first block finishes. Presumably because the call to getDataWithURL
, is itself asyncronous. What's the best way to make sure that the first block doesn't complete before that request returns. Should I try using an NSInvocation
to put the data inside returnData
?
回答1:
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
[someObject someSelector];
}];
This creates a 'standard' block operation. This operation is considered finished ([block1 isFinished]
is true) after the block is executed, even though the async network operation isn't complete from user point of view.
Since you don't want this, you need to subclass NSOperation
and explicitly tell it when your operation finishes by overriding its start
:
- (void)completeOperation {
self.finished = YES;
self.executing = NO;
}
- (void)start {
if ([self isCancelled]) {
[self completeOperation];
return;
}
self.executing = YES;
[self main];
// this is where operation is set to finished in NSOperation
}
- (void)main {
[someObject someSelectorWithCompletionBlock: ^() {
[self completeOperation];
// retain cycle may exist unless completion block is destroyed afterwards
}];
}
回答2:
I personally prefer grand central dispatch, as it makes the code pretty self-explanatory. This code will call the two selectors in sequence (from another thread) and then call doneSelector
on the main thread:
dispatch_async(dispatch_get_global_queue(0,0), ^{
// These will be called in sequence on a background thread
[someObject someSelector];
[someObject anotherSelector]
dispatch_async(dispatch_get_main_queue(), ^{
// These will be called on the main thread after the above are done
[someObject doneSelector];
NSLog(@"Finished operation");
});
});
回答3:
If it works for your situation, I would just make things simpler and do the following.
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
[someObject someSelector];
[someObject anotherSelector];
}];
[queue addOperation:block1];
回答4:
When you first posted your other question, I guessed you might have been dealing with asynchronous network requests, and if you want operations with asynchronous network requests, one generally solves that wrapping such network request within a subclassed NSOperation
as I discussed in the answer to your other question (and as ilya subsequently also described here).
Your question here suggested that you are calling sendSynchronousRequest
. If that was all you were doing, then that NSOperation
subclassing pattern would not be needed, and you should be able to use addDependency
without incident. But, it looks like you discovered that getDataWithURL
was, itself, submitting that sendSynchronousRequest
to its own operation queue, effectively making it asynchronous. You correctly identified that if you get rid of that redundant operation queue logic from within getDataWithURL
, then then your problem is resolved and no NSOperation
subclassing is needed.
But, I wouldn't be too quick to dismiss this subclassed NSOperation
pattern, though, because sendSynchronousRequest
has some limitations with which you might to encumber yourself. Namely, those requests can't be canceled. Likewise, you can't get progress of those requests as they're underway, nor handle any advanced features like challenge-response authentication or the like. You might not need any of these features, but because of these limitations, many of us generally shy away from sendSynchronousRequest
(and sendAsynchronousRequest
, too).
Longer term, you might find yourself drawn to NSURLConnectionDataDelegate-based network calls which addresses these limitations. In which case, this NSOperation
subclass pattern may again become useful. Or, even better, consider using a framework like AFNetworking that provides an NSOperation
-based networking solution, but does a lot of this grunt work for you.
来源:https://stackoverflow.com/questions/20225804/nsoperationqueue-with-synchronous-nsurlconnection