Ожидание завершения блока завершения в запросе AFNetworking


Я делаю запрос JSON с AFNetworking, а затем вызываю [operation waittuntilfinished], чтобы дождаться операции и блоков успеха или отказа. Но, кажется, это правильно, хотя-с точки зрения сообщений журнала, я получаю "0", "3", "1" вместо того, чтобы "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");
4 7

4 ответа:

Это работает с помощью AFNetworking для настройки запросов, но делает синхронный вызов, а затем обрабатывает блоки завершения вручную. Очень просто. AFNetworking, похоже, не поддерживает это https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ , хотя работа вокруг достаточно проста.

#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

Это должно (почти) сработать. Ваш звонок в

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

, вероятно, не следует передавать [url path] параметру path:. В AFNetworking land этот путь является всем после базового url (например, базовый url может быть "http://google.com" и путь "/ gmail " или что-то еще).

Тем не менее, это, вероятно, не очень хорошая идея, чтобы превратить асинхронную операцию в блокирующую поток синхронную операцию с waitUntilFinished, но я уверен, что у вас есть свои причины... ;)

Я просто столкнулся с той же проблемой и нашел другое решение. У меня было две операции, которые зависят друг от друга, но могут загружаться параллельно. Однако блок завершения второй операции не может быть выполнен до завершения блока завершения первой операции.

Как отметил Колин, это может быть плохой выбор, чтобы сделать веб-запрос блок. Это было очень важно для меня, поэтому я делал это асинхронно.

Вот мое решение:

// 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

Использовать вызов метода делегата

Поместите метод внутри блока, который будет вызывать себя при завершении загрузки / выгрузки.