Trabajar con la clase NSOperationQueue

La multitarea evita que las aplicaciones se congelen. En la mayoría de los lenguajes de programación, lograr esto es un poco complicado, pero la clase NSOperationQueue en iOS lo hace fácil!

Este tutorial le mostrará cómo usar el NSOperationQueue clase. Un objeto NSOperationQueue es una cola que maneja objetos de la NSOperation tipo de clase Un objeto NSOperation, simplemente expresado, representa una sola tarea, incluidos los datos y el código relacionado con la tarea. NSOperationQueue maneja y administra la ejecución de todos los objetos NSOperation (las tareas) que se le han agregado. La ejecución se lleva a cabo con el hilo principal de la aplicación. Cuando se agrega un objeto NSOperation a la cola, se ejecuta inmediatamente y no abandona la cola hasta que finaliza. Una tarea se puede cancelar, pero no se elimina de la cola hasta que se termina. La clase NSOperation es abstracta, por lo que no se puede utilizar directamente en el programa. En cambio, hay dos subclases proporcionadas, la NSInvocationOperation clase y la NSBlockOperation clase. Usaré el primero en este tutorial..


El proyecto de muestra

Este es el objetivo de este tutorial: para cada subproceso adicional queremos que nuestra aplicación cree un objeto NSInvocationOperation (NSOperation). Agregaremos cada objeto en NSOperationQueue y luego habremos terminado. La cola se encarga de todo y la aplicación funciona sin congelarse. Para demostrar claramente el uso de las clases que mencioné anteriormente, crearemos un proyecto de muestra (simple) en el que, además del subproceso principal de la aplicación, tendremos otros dos subprocesos que se ejecutan junto con él. En el primer hilo, un bucle se ejecutará de 1 a 10.000.000 y cada 100 pasos se actualizará una etiqueta con el valor del contador del bucle. En el segundo hilo, el fondo de una etiqueta se llenará con un color personalizado. Ese proceso tendrá lugar dentro de un bucle y se ejecutará más de una vez. Así tendremos algo así como un rotador de color. Al mismo tiempo, los valores RGB del color de fondo personalizado junto con el valor del contador de bucle se mostrarán junto a la etiqueta. Finalmente, usaremos tres botones para cambiar el color de fondo de la vista en el hilo principal. Estas tareas no podrían ejecutarse simultáneamente sin realizar múltiples tareas. Aquí hay un vistazo al resultado final:


Paso 1: Crea el Proyecto

Comencemos creando el proyecto. Abre el Xcode y crea un nuevo Solicitud de vista única.

Haga clic en Siguiente y establezca un nombre para el proyecto. Lo nombré ThreadingTestApp. Puedes usar el mismo o cualquier otro nombre que quieras.

Siguiente. completa la creación del proyecto.


Paso 2: Configurar la interfaz

Haga clic en el ViewController.xib Archivo para revelar el generador de interfaz. Agrega los siguientes controles para crear una interfaz como la siguiente imagen:

  1. UINavigationBar
    • Cuadro (x, y, W, H): 0, 0, 320, 44
    • Tintcolor: color negro
    • Título: "Demostración multihilo simple"
  2. UILabel
    • Cuadro (x, y, W, H): 20, 59, 280, 21
    • Texto: "Contador en el hilo # 1"
  3. UILabel
    • Cuadro (x, y, W, H): 20, 88, 280, 50
    • Color de fondo: Color gris claro.
    • Color del texto: color gris oscuro
    • Texto: -
  4. UILabel
    • Cuadro (x, y, W, H): 20, 154, 280, 21
    • Texto: "Random Color Rotator at Thread # 2"
  5. UILabel
    • Cuadro (x, y, W, H): 20, 183, 100, 80
    • Color de fondo: Color gris claro.
    • Texto: -
  6. UILabel
    • Cuadro (x, y, W, H): 128, 183, 150, 80
    • Texto: -
  7. UILabel
    • Cuadro (x, y, W, H): 20, 374, 280, 21
    • Texto: "Color de fondo en el hilo principal"
  8. UIButton
    • Cuadro (x, y, W, H): 20, 403, 73, 37
    • Título: "Color # 1"
  9. UIButton
    • Cuadro (x, y, W, H): 124, 403, 73, 37
    • Título: "Color # 2"
  10. UIButton
    • Cuadro (x, y, W, H): 228, 403, 73, 37
    • Título: "Color # 3"

Para la última UILabel y las tres UIButtons, configure el Autosize valor para Abajo a la izquierda para hacer que la interfaz se vea bien en el iPhone 4 / 4S y el iPhone 5, como en la siguiente imagen:


Paso 3: Propiedades de IBOutlet y métodos de acción de IB

En este próximo paso, crearemos las propiedades de IBOutlet y los métodos de IBAction que son necesarios para hacer que nuestra aplicación de ejemplo funcione. Para crear nuevas propiedades y métodos, y conectarlos a sus controles mientras se crea el Interface Builder, haga clic en el botón central del botón Editor en la barra de herramientas de Xcode para revelar la Editor asistente:

No todos los controles necesitan una propiedad de salida. Añadiremos solo uno para los UILabels. 3, 5 y 6 (De acuerdo con el orden en que fueron enumerados en el paso 2), llamado label1, label2 y label3.

Para insertar una nueva propiedad outlet, Control + clic (clic derecho) en una etiqueta> haga clic en la nueva salida de referencia> arrastre y suelte en el editor asistente. Después de eso, especifique un nombre para la nueva propiedad, como en las siguientes imágenes:

Insertar una nueva propiedad de IBOutlet


Configuración del nombre de la propiedad IBOutlet

Repita el proceso anterior tres veces para conectar los tres UILabels a las propiedades. Dentro de tu ViewController.h archivo tienes estas propiedades declaradas:

 @ propiedad (retener, no atómica) IBOutlet UILabel * label1; @ propiedad (retener, no atómica) IBOutlet UILabel * label2; @ propiedad (retener, no atómica) IBOutlet UILabel * label3;

Ahora agregue los métodos IBAction para los tres UIButtons. Cada botón cambiará el color de fondo de la vista. Insertar un nuevo método IBAction., Control + clic (clic derecho) en un UIButton> haga clic en el retoque interior> arrastre y suelte en el editor asistente. Después de eso, especifique un nombre para el nuevo método. Eche un vistazo a las siguientes imágenes y el siguiente fragmento de código para los nombres de los métodos:


Insertando un nuevo método IBAction
Configuración del nombre del método IBAction

De nuevo, repita el proceso anterior tres veces para conectar cada UIButton a un método de acción. los ViewController.h archivo ahora debe contener estos:

 - (IBAction) applyBackgroundColor1; - (IBAction) applyBackgroundColor2; - (IBAction) applyBackgroundColor3;

Las propiedades de IBOutlet y los métodos IBAction ya están listos. Ahora podemos empezar a codificar..


Paso 4: El objeto NSOperationQueue y las declaraciones de métodos necesarios relacionados con la tarea

Una de las tareas más importantes que debemos hacer es declarar un NSOperationQueue objeto (nuestra cola de operaciones), que se utilizará para ejecutar nuestras tareas en subprocesos secundarios. Abre el ViewController.h archivo y agregar el siguiente contenido justo después de la @interfaz encabezadono te olvides de los corchetes):

 @interface ViewController: UIViewController NSOperationQueue * operationQueue; 

Además, cada tarea debe tener al menos un método que contenga el código que se ejecutará simultáneamente con el hilo principal. De acuerdo a la descripción introductoria, la primera tarea del método será nombrada. contrapunto y el segundo será nombrado colorRotatorTask:

 -(void) counterTask; - (void) colorRotatorTask;

Eso es todo lo que necesitamos. Nuestro ViewController.h el archivo debería verse así:

 @interface ViewController: UIViewController NSOperationQueue * operationQueue;  @ propiedad (retener, no atómica) IBOutlet UILabel * label1; @ propiedad (retener, no atómica) IBOutlet UILabel * label2; @ propiedad (retener, no atómica) IBOutlet UILabel * label3; - (IBAction) applyBackgroundColor1; - (IBAction) applyBackgroundColor2; - (IBAction) applyBackgroundColor3; - (void) counterTask; - (void) colorRotatorTask; @fin

Vamos a pasar a la implementación.


Paso 5: Implementación

Ya casi terminamos. Hemos configurado nuestra interfaz, hemos realizado todas las conexiones necesarias, hemos declarado cualquier IBAction y otros métodos necesarios y hemos establecido nuestra base. Ahora es tiempo de construir sobre ellos.

Abre el ViewController.m archiva y ve a la viewDidLoad método. La parte más importante de este tutorial tendrá lugar aquí. Vamos a crear una nueva NSOperationQueue instancia y dos NSOperation (NSInvocationOperation) objetos. Estos objetos encapsularán el código de los dos métodos que declaramos anteriormente y luego serán ejecutados por su cuenta por el NSOperationQueue. Aquí está el código:

 - (void) viewDidLoad [super viewDidLoad]; // Crear una nueva instancia de NSOperationQueue. operationQueue = [NSOperationQueue new]; // Crear un nuevo objeto NSOperation utilizando la subclase NSInvocationOperation. // Dígale que ejecute el método counterTask. NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget: auto selector: @selector (counterTask) object: nil]; // Agregar la operación a la cola y dejar que se ejecute. [operationQueue addOperation: operation]; [liberación de la operación]; // La misma historia que la anterior, solo díganle aquí para ejecutar el método colorRotatorTask. operation = [[NSInvocationOperation alloc] initWithTarget: auto selector: @selector (colorRotatorTask) objeto: nil]; [operationQueue addOperation: operation]; [liberación de la operación]; 

Todo este proceso es realmente simple. Después de crear el NSOperationQueue instancia, creamos un objeto NSInvocationOperation (operación). Establecemos su método de selección (el código que queremos ejecutar en un subproceso separado), y luego lo agregamos a la cola. Una vez que entra en la cola, inmediatamente comienza a correr. Después de eso, el objeto de operación puede liberarse, ya que la cola es responsable de manejarlo de ahora en adelante. En este caso, creamos otro objeto y lo usaremos de la misma manera para la segunda tarea (colorRotatorTask).

Nuestra siguiente tarea es implementar los dos métodos de selección. Comencemos escribiendo el contrapunto método. Contendrá un para bucle que se ejecutará para un gran número de iteraciones y cada 100 pasos el label1El texto se actualizará con el valor del contador de la iteración actual (yo). El código es simple, así que aquí está todo:

 -(void) counterTask // Haga un bucle GRANDE y cada 100 pasos le permite actualizar la etiqueta UILabel con el valor del contador. para (int i = 0; i<10000000; i++)  if (i % 100 == 0)  // Notice that we use the performSelectorOnMainThread method here instead of setting the label's value directly. // We do that to let the main thread to take care of showing the text on the label // and to avoid display problems due to the loop speed. [label1 performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"%d", i] waitUntilDone:YES];   // When the loop gets finished then just display a message. [label1 performSelectorOnMainThread:@selector(setText:) withObject:@"Thread #1 has finished." waitUntilDone:NO]; 

Tenga en cuenta que se recomienda como práctica recomendada (incluso por Apple) realizar actualizaciones visuales en la interfaz utilizando el hilo principal y no directamente desde un hilo secundario. Por lo tanto, el uso de la performSelectorOnMainThread El método es necesario en casos como este..

Ahora implementemos el colorRotatorTask método:

 -(void) colorRotatorTask // Necesitamos un color personalizado para trabajar. UIColor * customColor; // Ejecutar un bucle con 500 iteraciones. para (int i = 0; i<500; i++)  // Create three float random numbers with values from 0.0 to 1.0. float redColorValue = (arc4random() % 100) * 1.0 / 100; float greenColorValue = (arc4random() % 100) * 1.0 / 100; float blueColorValue = (arc4random() % 100) * 1.0 / 100; // Create our custom color. Keep the alpha value to 1.0. customColor = [UIColor colorWithRed:redColorValue green:greenColorValue blue:blueColorValue alpha:1.0]; // Change the label2 UILabel's background color. [label2 performSelectorOnMainThread:@selector(setBackgroundColor:) withObject:customColor waitUntilDone:YES]; // Set the r, g, b and iteration number values on label3. [label3 performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"Red: %.2f\nGreen: %.2f\nBlue: %.2f\Iteration #: %d", redColorValue, greenColorValue, blueColorValue, i] waitUntilDone:YES]; // Put the thread to sleep for a while to let us see the color rotation easily. [NSThread sleepForTimeInterval:0.4];  // Show a message when the loop is over. [label3 performSelectorOnMainThread:@selector(setText:) withObject:@"Thread #2 has finished." waitUntilDone:NO]; 

Puedes ver que usamos el performSelectorOnMainThread Método aquí también. El siguiente paso es el [NSThread sleepForTimeInterval: 0.4]; comando, que se utiliza para causar un breve retraso (0,4 segundos) en cada ejecución del bucle. Aunque no es necesario usar este método, es preferible usarlo aquí para disminuir la velocidad de cambio de color de fondo de la label2 UILabel (nuestro rotador de color). Además, en cada bucle creamos valores aleatorios para el rojo, el verde y el azul. Luego, establecemos estos valores para producir un color personalizado y lo establecemos como un color de fondo en el label2 UILabel.

En este punto, las dos tareas que se ejecutarán al mismo tiempo con el hilo principal están listas. Implementemos los tres (realmente fáciles) métodos de IBAction y luego estamos listos para comenzar. Como ya he mencionado, los tres UIButtons cambiarán el color de fondo de la vista, con el objetivo final de demostrar cómo el hilo principal puede ejecutarse junto con las otras dos tareas. Aquí están:

 - (IBAction) applyBackgroundColor1 [self.view setBackgroundColor: [UIColor colorWithRed: 255.0 / 255.0 green: 204.0 / 255.0 blue: 102.0 / 255.0 alpha: 1.0]];  - (IBAction) applyBackgroundColor2 [self.view setBackgroundColor: [UIColor colorWithRed: 204.0 / 255.0 green: 255.0 / 255.0 blue: 102.0 / 255.0 alpha: 1.0]];  - (IBAction) applyBackgroundColor3 [self.view setBackgroundColor: [UIColor whiteColor]]; 

¡Eso es! Ahora puede ejecutar la aplicación y ver cómo se pueden llevar a cabo tres tareas diferentes al mismo tiempo. Recuerde que cuando finaliza la ejecución de los objetos NSOperation, automáticamente saldrá de la cola..


Conclusión

Es posible que muchos de ustedes ya hayan descubierto que el código real para ejecutar una aplicación multitarea solo requiere unas pocas líneas de código. Parece que la mayor carga de trabajo es implementar los métodos necesarios que funcionan con cada tarea. Sin embargo, este método es una forma fácil de desarrollar aplicaciones de subprocesos múltiples en iOS.