ios objective-c nsoperation nsoperationqueue nsurlsession

ios - ¿Cómo uso NSOperationQueue con NSURLSession?



objective-c (6)

Con NSURLSession no agrega manualmente ninguna operación a una cola. Utiliza el método - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request en NSURLSession para generar una tarea de datos que luego se inicia (llamando al método de reanudación).

Se le permite proporcionar la cola de operaciones para que pueda controlar las propiedades de la cola y también usarla para otras operaciones si lo desea.

Cualquiera de las acciones habituales que desee realizar en una operación NSO (es decir, iniciar, pausar, detener, reanudar) que realice en la tarea de datos.

Para poner en cola hasta 50 imágenes para descargar, simplemente puede crear 50 tareas de datos que la sesión de NSURLS correctamente pondrá en cola.

Estoy intentando crear un descargador de imágenes masivas, donde las imágenes se pueden agregar a una cola sobre la marcha para descargar, y puedo averiguar el progreso y cuándo terminan la descarga.

A través de mi lectura, parece que NSOperationQueue para la funcionalidad de cola y NSURLSession para la funcionalidad de red parece ser mi mejor opción, pero estoy confundido en cuanto a cómo usar los dos en tándem.

Sé que agrego instancias de NSOperation a NSOperationQueue y se ponen en cola. Y parece que creo una tarea de descarga con NSURLSessionDownloadTask , y múltiple si necesito varias tareas, pero no estoy seguro de cómo unir las dos.

NSURLSessionDownloadTaskDelegate parece tener toda la información que necesito para el progreso de la descarga y las notificaciones de finalización, pero también debo poder detener una descarga específica, detener todas las descargas y tratar los datos que obtengo de la descarga.


Conceptualmente, NSURLSession es una cola de operaciones. Si reanuda una tarea NSURLSession y un punto de interrupción en el controlador de finalización, el seguimiento de la pila puede ser bastante revelador.

Aquí hay un extracto del siempre fiel tutorial de Ray Wenderlich sobre NSURLSession con una declaración NSLog añadida al punto de interrupción al ejecutar el controlador de finalización:

NSURLSession *session = [NSURLSession sharedSession]; [[session dataTaskWithURL:[NSURL URLWithString:londonWeatherUrl] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // handle response NSLog(@"Handle response"); // <-- breakpoint here }] resume];

Arriba, podemos ver que el controlador de finalización se está ejecutando en la Thread 5 Queue: NSOperationQueue Serial Queue .

Entonces, supongo que cada NSURLSession mantiene su propia cola de operaciones, y cada tarea añadida a una sesión se ejecuta, bajo el capó, como una operación NSO. Por lo tanto, no tiene sentido mantener una cola de operaciones que controle los objetos de NSURLSession o las tareas de NSURLSession.

NSURLSessionTask ya ofrece métodos equivalentes, como cancel , resume , suspend , etc.

Es cierto que hay menos control de lo que tendría con su propia NSOperationQueue. Pero, de nuevo, NSURLSession es una nueva clase cuyo propósito es, indudablemente, relevarlo de esa carga.

En pocas palabras: si quiere menos problemas, pero tiene menos control, y confía en Apple para que realice las tareas de red de manera competente en su nombre, use NSURLSession. De lo contrario, ejecute el suyo con NSURLConnection y sus propias colas de operaciones.


Quizás estás buscando esto:

http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/

Es un poco raro que esto no sea ''incorporado'', pero si quieres conectar cosas NSURL con NSOperation, parece que tienes que reutilizar el runloop en el hilo principal y hacer que la operación sea ''concurrente'' (''concurrent ''a la cola).

Aunque en su caso, si se trata de descargas simples, sin operaciones dependientes posteriores conectadas, no estoy seguro de qué ganaría con el uso de NSOperation.


Si está utilizando OperationQueue y no desea que cada operación cree muchas solicitudes de red simultáneas, simplemente puede llamar a queue.waitUntilAllOperationsAreFinished () después de que cada operación se haya agregado a la cola. Ahora solo se ejecutarán después de que se complete el anterior, lo que reduce significativamente la cantidad de conexiones de red simultáneas.


Tu intuición aquí es correcta. Si se emiten muchas solicitudes, tener una NSOperationQueue con maxConcurrentOperationCount de 4 o 5 puede ser muy útil. En ausencia de eso, si emite muchas solicitudes (por ejemplo, 50 imágenes grandes), puede sufrir problemas de tiempo de espera al trabajar en una conexión de red lenta (por ejemplo, algunas conexiones celulares). Las colas de operaciones también tienen otras ventajas (por ejemplo, dependencias, asignación de prioridades, etc.), pero el control del grado de concurrencia es el beneficio clave, en mi humilde opinión.

Si está utilizando solicitudes basadas en la aplicación, la implementación de la solución basada en operaciones es bastante trivial (es la implementación de la subclase de NSOperation concurrente típica; consulte la sección Configuración de operaciones para la ejecución simultánea del capítulo Operaciones en cola de la Guía de programación de simultaneidad para obtener más información).

Si está utilizando la implementación basada en delegate , sin embargo, las cosas comienzan a ponerse bastante peludas rápidamente. Esto se debe a una característica comprensible (pero increíblemente molesta) de NSURLSession mediante la cual los delegados a nivel de tarea se implementan a nivel de sesión. (Piénselo: dos solicitudes diferentes que requieren un manejo diferente llaman al mismo método de delegado en el objeto de sesión compartido. ¡Egad!)

Puede hacerse una NSURLSessionTask de NSURLSessionTask basada en NSURLSessionTask en una operación (yo y otros, estoy seguro, lo he hecho), pero implica un proceso engorroso de tener el objeto de sesión manteniendo un diccionario haciendo referencia cruzada a identificadores de tareas con objetos de operación de tareas, hacer que pase estos métodos de delegación de tareas pasados ​​al objeto de la tarea, y luego hacer que los objetos de la tarea se ajusten a los diversos protocolos delegados NSURLSessionTask . Es una cantidad de trabajo bastante importante porque NSURLSession no proporciona una maxConcurrentOperationCount estilo maxConcurrentOperationCount en la sesión (por no hablar de otras bondades de NSOperationQueue , como dependencias, bloques de finalización, etc.).

Y vale la pena señalar que la implementación basada en la operación es un poco incipiente con las sesiones en segundo plano. Sus tareas de carga y descarga continuarán funcionando después de que la aplicación haya finalizado (lo cual es bueno, es un comportamiento bastante esencial en una solicitud de fondo), pero cuando se reinicia, la cola de operaciones y todas sus operaciones se han ido. . Por lo tanto, debe usar una implementación NSURLSession pura basada en NSURLSession para sesiones en segundo plano.


Actualización: las propiedades de executing y finishing mantienen el conocimiento sobre el estado de la NSOperation actual. Una vez que finishing está configurado en YES y executing en NO , su operación se considera como terminada. La forma correcta de NSOperation no requiere un dispatch_group y simplemente se puede escribir como una NSOperation asincrónica:

- (BOOL) isAsynchronous { return YES; } - (void) main { // We are starting everything self.executing = YES; self.finished = NO; NSURLSession * session = [NSURLSession sharedInstance]; NSURL *url = [NSURL URLWithString:@"http://someurl"]; NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){ /* Do your stuff here */ NSLog("Will show in second"); self.executing = NO; self.finished = YES; }]; [dataTask resume] }

El término asynchronous es bastante engañoso y no se refiere a la diferenciación entre el subproceso UI (principal) y el subproceso de fondo.

Si isAsynchronous está establecido en YES , significa que alguna parte del código se ejecuta de forma asincrónica con respecto al método main . Dicho de otra manera: se realiza una llamada asincrónica dentro del método main y el método finalizará una vez que finalice el método principal .

Tengo algunas diapositivas sobre cómo manejar la concurrencia en Apple OS: https://speakerdeck.com/yageek/concurrency-on-darwin .

Respuesta anterior : puede probar el dispatch_group_t . Puedes pensar que ellos retienen el contador para GCD.

Imagine el siguiente código en el método main de su subclase NSOperation :

- (void) main { self.executing = YES; self.finished = NO; // Create a group -> value = 0 dispatch_group_t group = dispatch_group_create(); NSURLSession * session = [NSURLSession sharedInstance]; NSURL *url = [NSURL URLWithString:@"http://someurl"]; // Enter the group manually -> Value = Value + 1 dispatch_group_enter(group); ¨ NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){ /* Do your stuff here */ NSLog("Will show in first"); //Leave the group manually -> Value = Value - 1 dispatch_group_leave(group); }]; [dataTask resume]; // Wait for the group''s value to equals 0 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog("Will show in second"); self.executing = NO; self.finished = YES; }