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);
Je vous recommande de consulter les sessions WWDC sur les applications réseau dans iPhone OS.
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.
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! */
}];
Créez une NSOperation simultanée sur laquelle vous exécutez votre NSURLConnection asynchrone.
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;
}