NSOperationQueue with synchronous NSURLConnection

女生的网名这么多〃 提交于 2019-12-13 04:35:30

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!