NSOperation y NSOperationQueue hilo de trabajo frente a hilo principal

Tengo que llevar a cabo una serie de operaciones de descarga y escritura de bases de datos en mi aplicación. Estoy usando NSOperation y NSOperationQueue para lo mismo.

Este es el escenario de la aplicación:

  • Obtener todos los códigos postales de un lugar.
  • Para cada código postal busque todas las casas.
  • Para cada casa, busque los detalles del habitante

Como dije, he definido una NSOperation para cada tarea. En el primer caso (Tarea 1), estoy enviando una solicitud al servidor para recuperar todos los códigos postales. El delegado dentro de NSOperation recibirá los datos. Estos datos luego se escriben en la base de datos. La operación de la base de datos se define en una clase diferente. Desde la clase NSOperation hago una llamada a la función de escritura definida en la clase de base de datos.

Mi pregunta es si la operación de escritura de la base de datos ocurre en el hilo principal o en un hilo de fondo. Como lo llamaba dentro de una NSOperation , esperaba que se ejecutara en un hilo diferente (Not MainThread) como NSOperation . ¿Puede alguien explicar este escenario al tratar con NSOperation y NSOperationQueue ?

Mi pregunta es si la operación de escritura de la base de datos ocurre en el hilo principal o en un hilo de fondo.

Si crea un NSOperationQueue desde cero, como en:

 NSOperationQueue *myQueue = [[NSOperationQueue alloc] init]; 

Estará en un hilo de fondo:

Las colas de operación generalmente proporcionan los hilos usados ​​para ejecutar sus operaciones. En OS X v10.6 y posterior, las colas de operaciones utilizan la biblioteca libdispatch (también conocida como Grand Central Dispatch) para iniciar la ejecución de sus operaciones. Como resultado, las operaciones siempre se ejecutan en un subproceso separado , independientemente de si se designan como operaciones simultáneas o no simultáneas.

A menos que esté usando la mainQueue :

 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; 

También puedes ver código como este:

 NSOperationQueue *myQueue = [[NSOperationQueue alloc] init]; [myQueue addOperationWithBlock:^{ // Background work [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // Main thread work (UI usually) }]; }]; 

Y la versión de GCD:

 dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { // Background work dispatch_async(dispatch_get_main_queue(), ^(void) { // Main thread work (UI usually) }); }); 

NSOperationQueue brinda un control más NSOperationQueue con lo que desea hacer. Puede crear dependencias entre las dos operaciones (descargar y guardar en la base de datos). Para pasar los datos entre un bloque y el otro, puede suponer, por ejemplo, que un NSData vendrá del servidor por lo que:

 __block NSData *dataFromServer = nil; NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init]; __weak NSBlockOperation *weakDownloadOperation = downloadOperation; [weakDownloadOperation addExecutionBlock:^{ // Download your stuff // Finally put it on the right place: dataFromServer = .... }]; NSBlockOperation *saveToDataBaseOperation = [[NSBlockOperation alloc] init]; __weak NSBlockOperation *weakSaveToDataBaseOperation = saveToDataBaseOperation; [weakSaveToDataBaseOperation addExecutionBlock:^{ // Work with your NSData instance // Save your stuff }]; [saveToDataBaseOperation addDependency:downloadOperation]; [myQueue addOperation:saveToDataBaseOperation]; [myQueue addOperation:downloadOperation]; 

Editar: Por qué estoy usando la referencia __weak para las Operaciones, se puede encontrar aquí . Pero en pocas palabras es para evitar retener los ciclos.

Si desea realizar la operación de escritura de la base de datos en el hilo de fondo, debe crear un NSManagedObjectContext para ese hilo.

Puede crear el fondo NSManagedObjectContext en el método de inicio de su subclase de NSOperation relevante.

Compruebe los documentos de Apple para concurrencia con los datos principales.

También puede crear un NSManagedObjectContext que ejecuta las solicitudes en su propio hilo de fondo creando con NSPrivateQueueConcurrencyType y realizando las solicitudes dentro de su método performBlock: .

De NSOperationQueue

En iOS 4 y versiones posteriores, las colas de operaciones utilizan Grand Central Dispatch para ejecutar operaciones. Antes de iOS 4, crean subprocesos separados para operaciones no concurrentes y ejecutan operaciones concurrentes desde el hilo actual.

Asi que,

 [NSOperationQueue mainQueue] // added operations execute on main thread [NSOperationQueue new] // post-iOS4, guaranteed to be not the main thread 

En su caso, es posible que desee crear su propio “hilo de base de datos” subclasificando NSThread y enviándole mensajes con performSelector:onThread:

El hilo de ejecución de NSOperation depende de NSOperationQueue donde agregó la operación. Mire esta statement en su código –

 [[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class 

Todo esto supone que no has NSOperation enhebrar en el método main de NSOperation que es el monstruo real donde se escribieron las instrucciones de trabajo (se espera que estén).

Sin embargo, en el caso de operaciones concurrentes, el escenario es diferente. La cola puede engendrar un hilo para cada operación simultánea. Aunque no está garantizado y depende de los recursos del sistema frente a las demandas de recursos de operación en ese punto del sistema. Puede controlar la concurrencia de la cola de operaciones mediante su propiedad maxConcurrentOperationCount .

EDITAR

Encontré tu pregunta interesante e hice un análisis / registro yo mismo. Tengo NSOperationQueue creado en el hilo principal como este –

 self.queueSendMessageOperation = [[[NSOperationQueue alloc] init] autorelease]; NSLog(@"Operation queue creation. current thread = %@ \n main thread = %@", [NSThread currentThread], [NSThread mainThread]); self.queueSendMessageOperation.maxConcurrentOperationCount = 1; // restrict concurrency 

Y luego, continué para crear una NSOperation y la agregué usando addOperation. En el método principal de esta operación cuando revisé el hilo actual,

 NSLog(@"Operation obj = %@\n current thread = %@ \n main thread = %@", self, [NSThread currentThread], [NSThread mainThread]); 

no fue como hilo principal. Y descubrió que el objeto de hilo actual no es el objeto de hilo principal.

Por lo tanto, la creación personalizada de cola en el hilo principal (sin concurrencia entre su operación) no significa necesariamente que las operaciones se ejecutarán en serie en el hilo principal.

El resumen de los documentos es que las operations are always executed on a separate thread (la publicación iOS 4 implica colas de operaciones subyacentes de GCD).

Es trivial comprobar que se está ejecutando en un hilo no principal:

 NSLog(@"main thread? %@", [NSThread isMainThread] ? @"YES" : @"NO"); 

Cuando se ejecuta en un hilo, es trivial usar GCD / libdispatch para ejecutar algo en el hilo principal, ya sea que los datos centrales, la interfaz de usuario u otro código requieran ejecutarse en el hilo principal:

 dispatch_async(dispatch_get_main_queue(), ^{ // this is now running on the main thread }); 

Si está haciendo un subproceso no trivial, debe usar FMDatabaseQueue .