web-dev-qa-db-fra.com

NSURLConnection et dépêche centrale

Est-il conseillé d'envelopper NSUrlConnection dans des blocs de style gcd et de l'exécuter sur une file d'attente low_priority?

Je dois m'assurer que mes connexions ne se font pas sur le thread principal et que les connexions doivent être asynchrones. J'ai également besoin de plusieurs demandes simultanées pour y aller en même temps.

Si je choisis la route gcd, je ne suis pas sûr du thread sur lequel les méthodes NSUrlConnectionDelegate sont appelées. 

NSURLConnection s'appuie sur les délégués; ainsi, une fois la connexion établie, quelle que soit la classe d'encapsuleur qui la gère, elle devra appeler son appelant. Je ne suis pas sûr de savoir comment traiter tous les divers rappels qui sont invoqués lorsque la connexion commence/se termine:

- (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;

Dois-je simplement appeler les versions synchrones mais enveloppées dans des blocs gcd? Et si je souhaite annuler un appel, utilisez "dispatch_suspend"?

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

// If I need to cancel
dispatch_suspend(queue);
35
Jonas Anderson

Je vous recommande de consulter les sessions WWDC sur les applications réseau dans iPhone OS.

  • WWDC 2010 Session 207 - Applications réseau pour iPhone OS, partie 1
  • WWDC 2010 Session 208 - Applications réseau pour iPhone OS, partie 2

Le conférencier a dit

"Les fils sont mauvais ™"

pour la programmation réseau et il est recommandé d’utiliser la programmation réseau asynchrone avec RunLoop. Utilisez un thread d'arrière-plan (file d'attente simultanée Grand Central Dispatch) pour le traitement de données thread-safe, pas pour la programmation réseau.

À propos, les sessions Blocks et Grand Central Dispatch valent également la peine d'être vues.

  • WWDC 2010 Session 206 - Présentation des blocs et du Grand Central Dispatch sur iPhone
  • WWDC 2010 Session 211 - Simplifier le développement d'applications iPhone avec Grand Central Dispatch

J'ai écrit une classe wrapper pour NSURLConnection asynchrone.


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

Comment utiliser la classe 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
Kazuki Sakamoto

Créez une NSOperation simultanée sur laquelle vous exécutez votre NSURLConnection asynchrone.

1
Nyx0uf

Regardez ce bloc de code:

-(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
NSPratik