Puede AFNetworking devolver datos de forma sincrónica (dentro de un bloque)?


Tengo una función que usa AFJSONRequestOperation, y deseo devolver el resultado solo después de tener éxito. ¿Podría indicarme en la dirección correcta? Todavía estoy un poco desorientado con los bloques y AFNetworking específicamente.

-(id)someFunction{
    __block id data;

    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
        success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){
            data = json;
            return data; // won't work
        }
        failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){

        }];



    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation: operation];

    return data; // will return nil since the block doesn't "lock" the app.
}
Author: Shai Mishali, 2011-11-01

6 answers

Para bloquear la ejecución del subproceso principal hasta que se complete la operación, puede hacer [operation waitUntilFinished] después de agregarlo a la cola de operaciones. En este caso, no necesitaría el return en el bloque; establecer la variable __block sería suficiente.

Dicho esto, desaconsejaría fuertemente forzar operaciones asíncronas a métodos síncronos. Es difícil conseguir su cabeza alrededor a veces, pero si hay alguna manera que usted podría estructurar esto para ser asincrónico, que sería casi seguro que la manera de Ve.

 56
Author: mattt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-01 17:04:55

Estoy usando semáforos para resolver este problema. Este código se implementa en mi propia clase heredada de AFHTTPClient.

__block id result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSURLRequest *req = [self requestWithMethod:@"GET"
                                       path:@"someURL"
                                 parameters:nil];
AFHTTPRequestOperation *reqOp = [self HTTPRequestOperationWithRequest:req
                                                              success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                                                  result = responseObject;                                                                          
                                                                  dispatch_semaphore_signal(semaphore);
                                                              }
                                                              failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                                                  dispatch_semaphore_signal(semaphore);
                                                              }];
reqOp.failureCallbackQueue = queue;
reqOp.successCallbackQueue = queue;
[self enqueueHTTPRequestOperation:reqOp];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
return result;
 12
Author: Kasik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-02 10:57:32

Sugeriría que no haga un método síncrono con AFNetworking (o bloques en general). Un buen enfoque es que cree otro método y use los datos json del bloque de éxito como argumento.

- (void)methodUsingJsonFromSuccessBlock:(id)json {
    // use the json
    NSLog(@"json from the block : %@", json); 
}

- (void)someFunction {
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
        success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){
            // use the json not as return data, but pass it along to another method as an argument
            [self methodUsingJsonFromSuccessBlock:json];
        }
        failure:nil];

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation: operation];
}
 11
Author: Ikhsan Assaat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-01 17:20:23

Vale la pena señalar que algunas características de AFClient de AFNetworking todavía se pueden usar de manera sincrónica, lo que significa que aún puede usar sutilezas como encabezados de autorización y cargas de varias partes.

Por ejemplo:

NSURLRequest *request = [self.client requestWithMethod: @"GET"
                                                  path: @"endpoint"
                                            parameters: @{}];
NSHTTPURLResponse *response = nil;
NSError *error = nil;

NSData *responseData = [NSURLConnection sendSynchronousRequest: request
                                             returningResponse: &response
                                                         error: &error];

Recuerde verificar response.statusCode en este caso, ya que este método no considera los códigos de falla HTTP como errores.

 6
Author: joerick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-20 17:27:15

Agregue esto debajo del código con el que normalmente trabaja:

[operation start];
[operation waitUntilFinished];
// do what you want
// return what you want

Ejemplo:

+ (NSString*) runGetRequest:(NSString*)frontPath andMethod:(NSString*)method andKeys:(NSArray*)keys andValues:(NSArray*)values
{
    NSString * pathway = [frontPath stringByAppendingString:method];
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:pathway]];
    NSMutableDictionary * params = [[NSMutableDictionary alloc] initWithObjects:values forKeys:keys];
    NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
                                                            path:pathway
                                                      parameters:params];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) 
{
            // Success happened here so do what ever you need in a async manner
} 
failure:^(AFHTTPRequestOperation *operation, NSError *error) 
{
            //error occurred here in a async manner
}];
        [operation start];
        [operation waitUntilFinished];

         // put synchronous code here

        return [operation responseString];
}
 4
Author: user2976703,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-10 17:49:15

Para expandir/actualizar la respuesta de @Kasik. Puedes crear una categoría en AFNetworking como esta usando semáforos:

@implementation AFHTTPSessionManager (AFNetworking)

- (id)sendSynchronousRequestWithBaseURLAsString:(NSString * _Nonnull)baseURL pathToData:(NSString * _Nonnull)path parameters:(NSDictionary * _Nullable)params {
    __block id result = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:baseURL]];
    [session GET:path parameters:params progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
        result = responseObject;
        dispatch_semaphore_signal(semaphore);
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        dispatch_semaphore_signal(semaphore);
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    return result;
 }

@end

Si está llamando al bloque sync dentro de un bloque de finalización de otra solicitud de AFNetwork, asegúrese de cambiar la propiedad completionQueue. Si no lo cambia, el bloque síncrono llamará a la cola principal al completarse mientras ya está en la cola principal y bloqueará su aplicación.

+ (void)someRequest:(void (^)(id response))completion {
    AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:@""] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    dispatch_queue_t queue = dispatch_queue_create("name", 0);
    session.completionQueue = queue;
    [session GET:@"path/to/resource" parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
     NSDictionary *data = [session sendSynchronousRequestWithBaseURLAsString:@"" pathToData:@"" parameters:nil ];
      dispatch_async(dispatch_get_main_queue(), ^{
          completion (myDict);
      });
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    dispatch_async(dispatch_get_main_queue(), ^{
        completion (error);
    });
}];
 1
Author: Mark Bourke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-15 20:46:41