2011-09-14 17 views
29

NSData siempre ha tenido un método muy conveniente llamado +dataWithContentsOfURL:options:error:. Aunque es conveniente, también bloquea la ejecución del hilo actual, lo que significaba que era básicamente inútil para el código de producción (ignorando NSOperation). Utilicé este método con poca frecuencia, olvidé por completo que existía. Hasta hace poco.NSURLConnection contra NSData + GCD

La forma en que he estado tomando datos de los tubos es el enfoque estándar NSURLConnectionDelegate: escriba una clase de descarga que maneje los diversos métodos NSURLConnectionDelegate, construya algunos datos, maneje errores, etc. Normalmente haré este genérico suficiente para ser reutilizado para tantas solicitudes como sea posible.

Digamos que mi clase típica de descarga se ejecuta en algún lugar del estadio de 100 líneas. Eso son 100 líneas para hacer asincrónicamente lo que NSData puede hacer sincrónicamente en una línea. Para mayor complejidad, esa clase de descarga necesita un protocolo de delegado propio para comunicar terminación y errores a su propietario, y el propietario necesita implementar ese protocolo de alguna manera.

Ahora, introduzca Grand Central Dispatch, y yo puedo hacer algo tan increíblemente simple como:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { 

    NSData* data = [NSData dataWithContentsOfURL:someURL]; 
    // Process data, also async... 

    dispatch_async(dispatch_get_main_queue(), ^(void) { 
     // Back to the main thread for UI updates, etc. 
    }); 
}); 

Y puedo tirar ese lechón en cualquier lugar que quiero, justo en línea. No hay necesidad de una clase de descarga, no es necesario manejar los métodos de delegado de conexión: datos asíncronos fáciles en solo unas pocas líneas. La disparidad entre este enfoque y mi enfoque anterior al GCD es de una magnitud lo suficientemente grande como para activar la alarma de Demasiado Bueno para Ser Verdadero.

Por lo tanto, mi pregunta: ¿Hay alguna advertencia al usar NSData + GCD para tareas simples de descarga de datos en lugar de NSURLConnection (asumiendo que no me preocupan cosas como el progreso de la descarga)?

+2

no puede cancelar la solicitud de uno .. – Daniel

Respuesta

22

Usted está perdiendo una gran cantidad de funcionalidad aquí:

  • no puede seguir la progresión de descarga
  • No se puede cancelar la descarga
  • No se puede gestionar el posible proceso de autenticación
  • Usted no puede manejar fácilmente los errores, que es realmente importante especialmente en el desarrollo móvil como en el iPhone, por supuesto (porque a menudo se pierde la red en condiciones reales, por lo que es muy importante rastrear tal red erro r casos al desarrollar para iOS)

y probablemente haya más, supongo.


El enfoque correcto para eso es crear una clase que gestione la descarga.

Ver mi propia clase OHURLLoader por ejemplo, que es simple y yo hicimos la API para ser fácil de usar con bloques:

NSURL* url = ... 
NSURLRequest* req = [NSURLRequest requestWithURL:url]; 

OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req]; 
[loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) { 
    NSLog(@"Download of %@ done (statusCode:%d)",url,httpStatusCode); 
    if (httpStatusCode == 200) { 
     NSLog(%@"Received string: %@", loader.receivedString); // receivedString is a commodity getter that interpret receivedData using the TextEncoding specified in the HTTP response 
    } else { 
     NSLog(@"HTTP Status code: %d",httpStatusCode); // Log unexpected status code 
    } 
} errorHandler:^(NSError *error) { 
    NSLog(@"Error while downloading %@: %@",url,error); 
}]; 

ver el proyecto README file y la muestra en github para obtener más información.

esta manera:

  • que todavía dependen de los métodos asincrónicos proporcionados por NSURLConnection (y as the Apple's documentation says about Concurrency Programming si una API que ya existe para hacer tareas asíncronas, lo utilizan en lugar de confiar en otra tecnología de roscado, si es posible)
  • mantiene las ventajas de NSURLConnection (gestión de errores, etc.)
  • pero también tiene las ventajas de la sintaxis de los bloques que hace que su código sea más legible que cuando usa los métodos de delegado
+0

Estoy de acuerdo con usted en la indicación del progreso, la cancelación y la autenticación. Todos los puntos buenos. El manejo de errores, por otra parte, habría esperado ser posible a través del error: argumento para el método. Si ese no es el caso, sin duda sería un inconveniente importante. –

+0

Puede manejar algunos errores si usa 'dataWithContentsOfURL: options: error:' pero no si usa 'dataWithContentsOfURL:' solo como en su código en su pregunta. Pero, además, solo tendrá errores de red, no HTTP, ni acceso a encabezados, etc. – AliSoftware

+0

+1 Gran respuesta. ¡Me encantó tu OHURLLoader, fue un verdadero placer usarlo! – Groot

12

WWDC 2010 Session Videos:

  • 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 conferenciante dijo

"Threads Are Evil™".

Para la programación en red, se recomienda encarecidamente utilizar API asíncrona con RunLoop.

Porque, si utiliza NSData + GCD como el siguiente, usa un hilo por conexión.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { 
    NSData* data = [NSData dataWithContentsOfURL:someURL]; 

Y es probable que utilice muchas conexiones y muchos subprocesos. Es demasiado fácil de usar GCD :-) Entonces, muchos hilos come gran cantidad de memoria para su pila. Por lo tanto, es mejor utilizar API asíncrona como dijo AliSoftware.

2

A partir de OS X v10.9 y iOS 7 la forma preferida es usar NSURLSession. Le ofrece una interfaz agradable basada en bloques y funciones como cancelar, suspender y descargar en segundo plano.