Red de conocimiento informático - Material del sitio web - Cómo utilizar NSOperations y NSOperationQueues

Cómo utilizar NSOperations y NSOperationQueues

Todos los desarrolladores de Apple probablemente se han encontrado con esta situación frustrante: haces clic en una aplicación de iOS o Mac, o haces clic en un botón o escribes texto, y la interacción del usuario de repente deja de responder.

En las aplicaciones móviles de iOS, los usuarios esperan que su aplicación responda instantáneamente a sus toques, pero una aplicación lenta o que no responde puede ser muy molesta y los usuarios a menudo reciben malos comentarios al respecto.

Sin embargo, es más fácil decirlo que hacerlo. Una vez que su aplicación necesita realizar múltiples tareas, las cosas pueden complicarse rápidamente. En el ciclo de ejecución principal, no hay mucho tiempo para realizar tareas pesadas mientras se utiliza una interfaz de usuario responsiva.

¿Qué deben hacer los desarrolladores que se encuentran en un dilema? Una forma es desacoplar algunas tareas del hilo principal mediante operaciones concurrentes. Las operaciones concurrentes significan que su programa puede ejecutar múltiples flujos (o subprocesos) simultáneamente en una sola operación, por lo que la interfaz interactiva sigue respondiendo mientras realiza las tareas.

Una forma de realizar operaciones concurrentes en iOS es utilizar las clases NSOperation y NSOperationQueue. ¡En este tutorial aprenderás a usarlos! Primero, creará una aplicación que no utilice subprocesos múltiples, por lo que se vuelve muy lenta. Luego mejorará el programa agregando operaciones paralelas, que con suerte brindarán al usuario una interfaz que responda mejor a las interacciones.

Antes de comenzar este tutorial, lea nuestro Tutorial de introducción a iOS Multithreading y Grand Central Dispatch. Sin embargo, dado que este tutorial es más general, no es necesario que lea este artículo.

Conocimientos previos

Antes de leer este tutorial, debes comprender algunos conceptos técnicos.

Quizás hayas oído hablar de la concurrencia y las operaciones paralelas. Desde una perspectiva técnica, la concurrencia es propiedad de los programas, mientras que las operaciones paralelas son propiedad de las máquinas. Paralelismo y concurrencia son dos conceptos diferentes. Como programador, no tiene garantía de que su código se ejecute en una máquina que pueda ejecutarlo en paralelo. Sin embargo, puede diseñar su código para utilizar operaciones simultáneas.

Primero, es necesario definir algunos términos:

Tarea: una tarea simple y única que debe completarse.

Subproceso: Mecanismo proporcionado por el sistema operativo que permite ejecutar múltiples instrucciones simultáneamente en un solo programa.

Proceso: fragmento de código ejecutable que puede estar compuesto por varios subprocesos.

(Nota: en iPhone y Mac, la funcionalidad de subprocesos la proporciona POSIXThreadsAPI (o pthreads), que forma parte del sistema operativo. Esto es algo de muy bajo nivel y descubrirá que es fácil cometer errores; tal vez lo peor de los subprocesos es que son extremadamente difíciles de detectar.

El marco Foundation incluye una clase llamada NSThread que es más fácil de manejar, pero administrar múltiples subprocesos con NSThread sigue siendo un dolor de cabeza.

NSOperation y NSOperationQueue son clases de nivel superior que simplifican enormemente el proceso de trabajar con múltiples subprocesos)

En este diagrama, puede ver las conexiones entre procesos, subprocesos y tareas. Relación:

En este diagrama, el Hilo 2 realiza la operación de leer el archivo, mientras que el Hilo 1 realiza el código relacionado con la interfaz de usuario. Esto es muy similar a cómo estructura su código en iOS: el hilo principal debe realizar cualquier tarea relacionada con la interfaz de usuario, y luego el hilo secundario debe realizar operaciones lentas o largas (como leer archivos, acceder a la red, etc.)

NSOperationvs.GrandCentralDispatch(GCD)

Es posible que haya oído hablar de GrandCentralDispatch (GCD). En resumen, GCD consta de funciones de lenguaje, bibliotecas de tiempo de ejecución y mejoras del sistema (que proporcionan mejoras integrales y del sistema para admitir operaciones simultáneas en hardware de múltiples núcleos en iOS y OSX).

Si desea obtener más información sobre GCD, puede leer Introducción a iOS Multithreading y Grand Central Dispatch.

Antes de MacOSXv10.6 e iOS4, la diferencia entre NSOperation, NSOperationQueue y GCD es que utilizan mecanismos completamente diferentes. A partir de MacOSXv10.6 e iOS4, NSOperation y NSOperationQueue se basan en GCD. Como regla general, Apple recomienda utilizar el nivel más alto de abstracción, pero de repente bajará a un nivel más bajo cuando la evaluación muestre la necesidad.

La siguiente es una comparación rápida de los dos, que le ayudará a decidir cuándo y dónde usar GCD o NSOperation y NSOperationQueue;

GCD es un método ligero para representar unidades de tareas. que se ejecutará simultáneamente. No es necesario que planifiques estas unidades de tareas, el sistema lo hace por ti. Agregar dependencias a los bloques puede ser un dolor de cabeza. ¡Cancelar o pausar bloques crea trabajo extra para los desarrolladores!

NSOperation y NSOperationQueue tienen una pequeña sobrecarga adicional en comparación con GCD, pero puedes agregar dependencias para múltiples operaciones. Puede reutilizar una acción, cancelarla o pausarla. NSOperation es compatible con la observación de valores clave (KVO); por ejemplo, puede escuchar NSNotificationCenter para que una operación comience a ejecutarse.

Modelo preliminar del proyecto

En el modelo preliminar del proyecto, tiene una vista de tabla con un diccionario como fuente de datos. Las palabras clave del diccionario son los nombres de las imágenes y cada una; palabras clave El valor es la dirección URL de la imagen. El objetivo de este proyecto es leer el contenido del diccionario, descargar las imágenes, aplicar operaciones de filtro de imágenes y finalmente mostrar las imágenes en una vista de tabla.

El siguiente es un diagrama esquemático del modelo:

(Nota: si no desea crear primero una versión sin subprocesos del proyecto, pero desea ir directamente a En la dirección de subprocesos múltiples, puede omitir esta sección. Descargue la primera versión del proyecto que creamos en esta sección (todas las imágenes son de stock.xchng. Algunas imágenes en la fuente de datos tienen nombres incorrectos intencionalmente para proporcionar ejemplos de descargas de imágenes de prueba que p>Inicie Xcode y cree un nuevo proyecto usando la plantilla de aplicación iOSApplicationEmpty y haga clic en Siguiente, seleccione Usar recuento automático de referencias (no deje nada más marcado) y haga clic en Siguiente para guardar el proyecto en cualquier ubicación. >

Seleccione el proyecto ClassicPhoto en el Navegador de proyectos. Seleccione las fases de ClassicPhotosBuild de destino y expanda Vincular binario con bibliotecas. Utilice el botón + para agregar el marco de imagen principal.

p>

Cambie a AppDelegate. .h en Project Navigator e importe el archivo ListViewController, que servirá como controlador de vista raíz, que definirá en el siguiente paso

#import "ListViewController.h"

Cambie a AppDelegate.m, busque la aplicación: didFinishLaunchingWithOptions:, cree una instancia del objeto ListViewController y configúrelo como el controlador de vista raíz de UIWindow.

- (BOOL)aplicación:(UIApplication *)aplicación didFinishLaunchingWithOptions:( NSDictionary *)launchOptions {

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] límites ]];

self.window.window. backgroundColor = [UIColor whiteColor];

/*

ListViewController es una subclase de UITableViewController.

Mostraremos

Aquí envolvemos ListViewController en un UINavigationController y lo configuramos como el controlador de vista raíz.

Mostraremos la imagen en ListViewController.

Aquí envolvemos ListViewController en un UINavigationController y lo configuramos como el controlador de vista raíz.

ListViewController *listViewController = [[ListViewController alloc] initWithStyle:UITableViewStylePlain];

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:listViewController];

self .rootViewController = navController;

[self.window makeKeyAndVisible];

return YES;

}

Nota: agregarlo aquí desde la interfaz no ha sido creada antes, aquí te muestro que en lugar de usar un Storyboard o un archivo xib, creas la interfaz usando un programa. En este tutorial simplemente usaremos este enfoque.

A continuación se explica cómo crear una subclase ListViewController de UITableViewController. Cambie a ListViewController.h y realice algunas modificaciones:

/ 1

#import

#import <

// 2

#define kDatasourceURLString @"/downloads/ClassicPhotosDictionary.plist"

// 3

@interface ListViewController: UITableViewController

// 4

@property (nonatomic, strong) NSDictionary *photos; // La principal fuente de datos del controlador

@end

Ahora echemos un vistazo a El código anterior significa:

1. Introduzca UIKitandCoreImage, que es el archivo de encabezado de importación.

2. Por conveniencia, definimos la macro kDatasourceURLString, que es la cadena de dirección de la fuente de datos.

3. Luego haga de ListViewController una subclase de UITableViewController, es decir, use NSObject en lugar de UITableViewController.

4. Declare una instancia de objeto NSDictionary, que también es la fuente de datos.

Ahora cambie a ListViewController.m y realice los siguientes cambios:

@implementation ListViewController

// 1

@synthesize fotos = _photos;

#pragma mark -

#pragma mark - creación de instancias diferida

// 2

- (NSDictionary *)photos {

if (!_photos) {

NSURL *dataSourceURL = [NSURL URLWithString:kDatasourceURLString];

_photos = [[NSDictionary alloc] initWithContentsOfURL:dataSourceURL] ;

}

return _photos;

}

#pragma mark -

#pragma mark - período de vida

- (void)viewDidLoad {

// 3

self.title = @"Fotos clásicas";

// 4

self.tableView.rowHeight = 80.0;

[super viewDidLoad];

}

- (void)viewDidUnload {< / p>

// 5

[self setPhotos: nil];

[super viewDidUnload];

}

# marca pragma -

#pragma mark - fuente de datos UITableView y método delegado

// 6

- (NSInteger)tableView:(UITableView *) tableView numberOfRowsInSection : (NSInteger)sección {

NSInteger count = self.photo.

count;

return count;

}

// 7

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath {

retorno 80.0;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {

NSString estático * kCellIdentifier = @"identificador de celda";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];

if (!cell ) {

cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellIdentifier];

cell .selectionStyle = UITableViewCellSelectionStyleNone;

}

// 8

NSString *rowKey = [[self.photos allKeys] objectAtIndex.imageData];

imagen = [self applySepiaFilterToImage:unfiltered_image];

}

cell.textLabel.text=rowKey;

cell.imageView.image=imagen;

cell.textLabel.text=rowKey;