Objective-C: POST asíncrono / de background sin utilizar el método delegado?

Necesito hacer algunas llamadas POST a mi server, pero no necesito bloquear el hilo principal. Según entiendo, NSMutableURLRequest y NSURLConnection no son seguros para subprocesss, por lo que es mejor utilizar el método asynchronous de NSURLConnection .

Mi pregunta sobre esto es, ¿cómo puedo empaquetarlo bien en un método, en lugar de tener que usar el método de delegado? Yo preferiría hacer:

 NSData *returnedData = [Utility postDataToURL:@"some string of data"]; 

Así es fácil de hacer con el siguiente método:

 [NSURLConnection sendSynchronousRequest:serviceRequest returningResponse:&serviceResponse error:&serviceError]; 

¡Es tan agradable mantener todo dentro de un solo método, luego solo tener mis datos devueltos!

¿Hay algún método basado en bloque para esto? Se convierte en un problema cuando necesito escribir methods para unas 50 llamadas diferentes y cada uno necesita usar el mismo método de delegado. ¿Lo estoy haciendo de la manera incorrecta?

Esto solo tendrá que ser para iOS5.

iOS 5 agrega sendAsynchronousRequest:queue:completionHandler: que hace lo que creo que quieres. Tengo mi código configurado para usar, si está disponible, pero para volver a realizar una búsqueda síncrona en una queue GCD de background y saltar al hilo principal con el resultado si no lo hace. Este último será less eficiente de energía, pero es solo para mantener el soporte henetworkingado.

 if([NSURLConnection respondsToSelector:@selector(sendAsynchronousRequest:queue:completionHandler:)]) { // we can use the iOS 5 path, so issue the asynchronous request // and then just do whatever we want to do [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler: ^(NSURLResponse *response, NSData *data, NSError *error) { [self didLoadData:data]; }]; } else { // fine, we'll have to do a power inefficient iOS 4 implementation; // hop onto the global dispatch queue... dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // ... perform a blocking, synchronous URL retrieval ... NSError *error = nil; NSURLResponse *urlResponse = nil; NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error]; // ... and hop back onto the main queue to handle the result dispatch_async(dispatch_get_main_queue(), ^{ [self didLoadData:responseData]; }); }); } 

En el código de producción realmente verificará los error y los códigos de respuesta HTTP (como respuesta del server 404 probablemente sea tanto un error desde su punto de vista como una falla de connection), obviamente.

iOS 5.0 > puedes usar el método sendAsynchronousRequest mira NSURLConnection Class y usa bloques. Si desea admitir iOS 4.0 > también, debe escribir una de sus propias descargas de URL asíncrona basada en bloques, que es bastante fácil de escribir. Está mejor utilizando MKNetworkKit .

pero no necesito bloquear el hilo principal. Según entiendo, NSMutableURLRequest y NSURLConnection no son seguros para subprocesss, por lo que es mejor utilizar el método asynchronous de NSURLConnection.

No desea hacer una connection de networking síncrona, bloquea el subprocess del que se llama (es aún peor si es el subprocess principal). Puede hacer una connection de networking asíncrona en el hilo principal. Si desea realizar una llamada a NSURLConnection en un hilo no principal, debe crear un RunLoop en ese hilo (si no lo hace, entonces los methods de delegado de NSURLConnection nunca se llamarán).

Tuve este problema pre-5.0, así que hice una pequeña class para manejar el protocolo de delegado NSURLConnection y ofrezco a los llamantes una interfaz con cierres:

BetterNSURLConnection.h

 @property (retain, nonatomic) NSURLRequest *request; 

BetterNSURLConnection.m

 @property (retain, nonatomic) NSURLConnection *connection; @property (retain, nonatomic) NSHTTPURLResponse *response; @property (retain, nonatomic) NSMutableData *responseData; @property (copy, nonatomic) void (^completionBlock)(id, NSHTTPURLResponse *); @property (copy, nonatomic) void (^errorBlock)(NSError *); 

… Puede agregar typedefs para hacer que esas firmas de bloque sean más bonitas … entonces:

 @synthesize connection = _connection; @synthesize response = _response; @synthesize responseData = _responseData; @synthesize completionBlock = _completionBlock; @synthesize errorBlock = _errorBlock; @synthesize request=_request; - (void)startWithCompletion:(void (^)(id, NSHTTPURLResponse *))completionBlock error:(void (^)(NSError *))errorBlock { [UIApplication shanetworkingApplication].networkActivityIndicatorVisible = YES; self.completionBlock = completionBlock; self.errorBlock = errorBlock; self.responseData = [NSMutableData data]; NSURLConnection *connection = [NSURLConnection connectionWithRequest:self.request delegate:self]; self.connection = connection; [self.connection start]; [connection release]; } 

… entonces haz el delegado así:

 - (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSHTTPURLResponse *)response { [self.responseData setLength:0]; self.response = response; } - (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data { [self.responseData appendData:data]; } - (void)connection:(NSURLConnection *)aConnection didFailWithError:(NSError *)error { [UIApplication shanetworkingApplication].networkActivityIndicatorVisible = NO; self.errorBlock(error); self.connection = nil; } - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection { [UIApplication shanetworkingApplication].networkActivityIndicatorVisible = NO; if (self.response.statusCode >= 400) { self.errorBlock(error); } else { // i do json requests, and call a json parser here, but you might want to do something different id result = [self parseResponse:self.responseData]; self.completionBlock(result, self.response); } self.connection = nil; } 

Utilizo un método de fachada para poner en queue a un trabajador interno que emite la llamada síncrona. Dependiendo de la velocidad a la que envíe las llamadas, podría funcionar. Ejemplo:

 // Presented in @interface -(void)sendPostRequest { // Last chance to update main thread/UI NSInvocationOperation *op = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(sendPostRequest_internal) object:nil] autorelease]; [opQueue addOperation:op]; } // Hidden in @implementation -(void)sendPostRequest_internal { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSURLRequest *request = // yadda, you might use NSURLMutableRequest NSURLResponse *response = nil; NSError *error = nil; NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:response error:error]; // process data, retain things as needed, post results using performSelectorOnMainThread: [pool release]; } 

Funciona bastante bien para mis propósitos, pero es posible que tengas que ahondar un poco más en las cosas asincrónicas, lo que realmente no es tan malo.