NSURLConnection y grand central dispatch


¿Es aconsejable envolver NSURLConnection en bloques de estilo gcd y ejecutarlo en una cola low_priority?

Necesito asegurarme de que mis conexiones no están sucediendo en el hilo principal y las conexiones deben ser asincrónicas. También necesito varias solicitudes simultáneas para ir a la vez.

Si voy por la ruta gcd, no estoy seguro de en qué hilo se invocan los métodos NSURLConnectionDelegate.

NSURLConnection depende de los delegados, por lo que una vez que se completa la conexión, cualquier clase wrapper que lo maneje necesitará invocar a su llamador. No estoy seguro de cómo tratar con todas las varias devoluciones de llamada que se invocan cuando el trabajo de conexión se inicia / finaliza:

- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)incrementalData;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;

¿Debo llamar a las versiones síncronas pero envueltas en bloques gcd? Y si quiero cancelar una llamada, utilizar 'dispatch_suspend'?

dispatch_async(queue,^{
      NSString* result = [self mySynchronousHttp:someURLToInvoke];
      });

// If I need to cancel
dispatch_suspend(queue);
Author: Jonas Anderson, 2011-02-18

3 answers

Le recomiendo que vea las sesiones WWDC sobre la aplicación de red en el sistema operativo iPhone.

  • WWDC 2010 Sesión 207 - Aplicaciones de red para iPhone OS, Parte 1
  • WWDC 2010 Sesión 208 - Aplicaciones de red para iPhone OS, Parte 2

El profesor dijo:

"Threads Are Evil™"

Para la programación de red y se recomienda utilizar la programación de red asíncrona con RunLoop. Usar subprocesos en segundo plano (Cola concurrente de Grand Central Dispatch) para subprocesos seguros procesamiento de datos, no para programación de redes.

Por cierto, también vale la pena ver los bloques y las sesiones de Grand Central Dispatch.

  • WWDC 2010 Session 206-Presentación de Bloques y Grand Central Dispatch en iPhone
  • WWDC 2010 Session 211-Simplificando el Desarrollo de Aplicaciones para iPhone con Grand Central Dispatch

Escribí una clase wrapper para asíncrono NSURLConnection.


AsyncURLConnection.h

#import <Foundation/Foundation.h>

typedef void (^completeBlock_t)(NSData *data);
typedef void (^errorBlock_t)(NSError *error);

@interface AsyncURLConnection : NSObject
{
    NSMutableData *data_;
    completeBlock_t completeBlock_;
    errorBlock_t errorBlock_;
}

+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
@end

AsyncURLConnection.m

#import "AsyncURLConnection.h"

@implementation AsyncURLConnection

+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{
    return [[[self alloc] initWithRequest:requestUrl
        completeBlock:completeBlock errorBlock:errorBlock] autorelease];
}

- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{

    if ((self=[super init])) {
        data_ = [[NSMutableData alloc] init];

        completeBlock_ = [completeBlock copy];
        errorBlock_ = [errorBlock copy];

        NSURL *url = [NSURL URLWithString:requestUrl];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        [NSURLConnection connectionWithRequest:request delegate:self];
    }

    return self;
}

- (void)dealloc
{
    [data_ release];

    [completeBlock_ release];
    [errorBlock_ release];
    [super dealloc];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [data_ setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [data_ appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    completeBlock_(data_);
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    errorBlock_(error);
}

@end

Cómo usar la clase AsyncURLConnection.

/*
 * AsyncURLConnection -request:completeBlock:errorBlock: have to be called
 * from Main Thread because it is required to use asynchronous API with RunLoop.
 */

[AsyncURLConnection request:url completeBlock:^(NSData *data) {

    /* success! */

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        /* process downloaded data in Concurrent Queue */

        dispatch_async(dispatch_get_main_queue(), ^{

            /* update UI on Main Thread */

        });
    });

} errorBlock:^(NSError *error) {

    /* error! */

}];
 53
Author: Kazuki Sakamoto,
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-03-01 06:57:21

Cree una NSOperation concurrente en la que ejecute su NSURLConnection asincrónica.

 1
Author: Nyx0uf,
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-02-19 11:36:45

Echa un vistazo a este bloque de código:

-(void)getDataFromServer
{
    NSDictionary *dicParams = [NSDictionary dictionaryWithObjectsAndKeys:
                           userId,    kUserID,
                           pageIndex,kPageIndex,
                           nil];

    NSString *yourURL = [self addQueryStringToUrlString:[NSString stringWithFormat:@"%@/%@",_PATH_,apiName] withDictionary:dicParams];


    NSString *queue_id = @"_queue_id_";
    dispatch_queue_t queue = dispatch_queue_create([queue_id UTF8String], 0);
    dispatch_queue_t main = dispatch_get_main_queue();

    dispatch_async(queue, ^{

        NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:yourURL] 
                                                    cachePolicy:NSURLRequestReloadIgnoringCacheData 
                                                timeoutInterval:60.0];
        [theRequest setHTTPMethod:@"GET"];
        [theRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
        [theRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

        NSError        *serviceError = nil;
        NSURLResponse  *serviceResponse = nil;
        NSData *dataResponse = [NSURLConnection sendSynchronousRequest:theRequest 
                                                     returningResponse:&serviceResponse 
                                                                 error:&serviceError];

        if(serviceError)
        {
            dispatch_sync(main, ^{

                // Update your UI

                if(serviceError.code == -1012){
                    // Log error
                }else{
                    // Log error
                }
            });
        }

        else
        {
            NSError *jsonError = nil;

            id dataObject = [NSJSONSerialization JSONObjectWithData:dataResponse 
                                                            options:kNilOptions 
                                                              error:&jsonError];
            NSMutableArray *arrResponse = (NSMutableArray *)dataObject;

            dispatch_sync(main, ^{

                // Update your UI
                [yourTableView reloadData];
            });
        }
    });
}

+(NSString*)addQueryStringToUrlString:(NSString *)urlString withDictionary:(NSDictionary *)dictionary
{
    NSMutableString *urlWithQuerystring = [[NSMutableString alloc] initWithString:urlString];

    for (id key in dictionary) {
        NSString *keyString = [key description];
        NSString *valueString = [[dictionary objectForKey:key] description];

        if ([urlWithQuerystring rangeOfString:@"?"].location == NSNotFound) {
            [urlWithQuerystring appendFormat:@"?%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
        } else {
            [urlWithQuerystring appendFormat:@"&%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
        }
    }
    return urlWithQuerystring;
}

+(NSString*)urlEscapeString:(NSString *)unencodedString
{
    CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString;
    NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, NULL,kCFStringEncodingUTF8);
    CFRelease(originalStringRef);
    return s;
}
 0
Author: NSPratik,
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
2015-07-31 13:41:20