Waiting for completion block to complete in an AFNetworking request

自作多情 提交于 2019-11-29 03:32:03

问题


I am making a JSON request with AFNetworking and then call [operation waitUntilFinished] to wait on the operation and the success or failure blocks. But, it seems to fall right though - in terms of the log messages, I get "0", "3", "1" instead of "0", "1", "3"

NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://google.com"]];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
httpClient.parameterEncoding = AFFormURLParameterEncoding;
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:@"query", @"q", nil];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:[url path] parameters:params];
NSLog(@"0");
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *innerRequest, NSHTTPURLResponse *response, id JSON) {
 NSLog(@"1");
 gotResponse = YES;
} failure:^(NSURLRequest *innerRequest, NSHTTPURLResponse *response, NSError *error, id JSON) {
  NSLog(@"2");
  gotResponse = YES;
}];
NSLog(@"Starting request");
[operation start];
[operation waitUntilFinished];
NSLog(@"3");

回答1:


This works by using AFNetworking to set up the requests, but making a synchronous call then handling the completion blocks manually. Very simple. AFNetworking doesn't seem to support this https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ, though the work around is simple enough.

#import "SimpleClient.h"

#import "AFHTTPClient.h"
#import "AFJSONRequestOperation.h"
#import "AFJSONUtilities.h"

@implementation SimpleClient

+ (void) makeRequestTo:(NSString *) urlStr
        parameters:(NSDictionary *) params
        successCallback:(void (^)(id jsonResponse)) successCallback 
        errorCallback:(void (^)(NSError * error, NSString *errorMsg)) errorCallback {

   NSURLResponse *response = nil;
   NSError *error = nil;

   NSURL *url = [NSURL URLWithString:urlStr];

   AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];

   httpClient.parameterEncoding = AFFormURLParameterEncoding;

   NSMutableURLRequest *request = [httpClient requestWithMethod:@"POST" path:[url path] parameters:params];
   NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

   if(error) {
      errorCallback(error, nil);
   } else {
      id JSON = AFJSONDecode(data, &error);
      successCallback(JSON);
   }
}

@end



回答2:


That should (almost) work. Your call to

NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:[url path] parameters:params];

should probably not pass [url path] to the path: parameter. In AFNetworking land, that path is everything after the base url (for example the base url could be "http://google.com" and the path "/gmail" or whatever).

That being said, it's probably not a good idea to make the asynchronous operation into a thread-blocking synchronous operation with waitUntilFinished, but I'm sure you have your reasons... ;)




回答3:


I just had the same problem and found a different solution. I had two operations that depend on each other, but can load in parallel. However, the completion block of the second operation can not be executed before the completion block of the first one has finished.

As Colin pointed out, it might be a bad choice to make a web request block. This was essential to me, so I did it asynchronously.

This is my solution:

// This is our lock
@interface SomeController () {
    NSLock *_dataLock;
}
@end

@implementation

// This is just an example, you might as well trigger both operations in separate
// places if you get the locking right
// This might be called e.g. in awakeFromNib
- (void)someStartpoint {
    AFJSONRequestOperation *operation1 = [AFJSONRequestOperation JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:url1]
                                                                                         success:^(NSURLRequest *request, NSHTTPURLResponse *response, id data) {
        // We're done, we unlock so the next operation can continue its
        // completion block
        [_dataLock unlock];
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id data) {
        // The request has failed, so we need to unlock for the next try
        [_dataLock unlock];
    }];

    AFJSONRequestOperation *operation2 = [AFJSONRequestOperation JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:url2]
                                                                                         success:^(NSURLRequest *request, NSHTTPURLResponse *response, id data) {
        // The completion block (or at least the blocking part must be run in a
        // separate thread
        [NSThread detachNewThreadSelector:@selector(completionBlockOfOperation2:) toTarget:self withObject:data];
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id data) {
        // This second operation may fail without affecting the lock
    }];

    // We need to lock before both operations are started
    [_dataLock lock];

    // Order does not really matter here
    [operation2 start];
    [operation1 start];
}

- (void)completionBlockOfOperation2:(id)data {
    // We wait for the first operation to finish its completion block
    [_dataLock lock];

    // It's done, so we can continue

    // We need to unlock afterwards, so a next call to one of the operations
    // wouldn't deadlock
    [_dataLock unlock];
}

@end



回答4:


Use Delegate method call

Put the method inside block which will call itself when downloading/uploading completes.



来源:https://stackoverflow.com/questions/10670892/waiting-for-completion-block-to-complete-in-an-afnetworking-request

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