db executeUpdate … en el bloque FMDB y no pasa, sin error

estoy usando el sorprendente proyecto FMDB en mi aplicación en desarrollo, tengo una NSOperation como esta:

- (void)main { @autoreleasepool { FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:[[NSUserDefaults standardUserDefaults] valueForKey:@"pathDB"]]; [queue inDatabase:^(FMDatabase *db) { FMResultSet *toQuery; if (self._id == nil) { toQuery = [db executeQuery:@"SELECT id,language,update_time FROM task"]; while ([toQuery next]) { [myarray addObject:[toQuery resultDictionary]]; } }]; for (int i = 0; i<[myarray count]; i++){ ...Do Something [queue inDatabase:^(FMDatabase *db) { FMResultSet *checkImgQuery = [db executeQuery:@"SELECT img_url,img_path FROM task WHERE id = ? AND language = ?",myTask.id ,myTask.lang]; while ([checkImgQuery next]) { if (![[checkImgQuery stringForColumn:@"img_url"] isEqualToString:myTask.img]) { NSData *my_img = [NSData dataWithContentsOfURL:[NSURL URLWithString:myTask.img]]; if (my_img != nil) { NSError *er; [my_img writeToFile:[checkImgQuery stringForColumn:@"img_path"] options:NSDataWritingAtomic error:&er]; //In the line under here the code block, the app still running, but this operation doesn't //go over this task [db executeUpdate:@"UPDATE task SET img_url = ? WHERE id = ? AND language = ?",myTask.img,[NSNumber numberWithInt:myTask.id],[NSNumber numberWithInt:myTask.language]; NSLog(@"%@",[db lastErrorMessage]); } ...Do Something } } } }]; } } 

El problema está en [db executeUpdate:...] que en algún momento funciona sin problemas y en algún momento se congela y no pasa por esa línea, el NSLog que puse allí no imprime nada, la aplicación no falla y continúa pero si el hilo está atascado allí, si cierro la ejecución de la aplicación y la vuelvo a reiniciar, el hilo no se detiene en la misma tarea, sino aleatoria en otra, sin criterios, alguna vez se trabaja y algunos el time no … ¿alguien puede ayudar?

Hay un par de problemas que me salpican, uno o más de los cuales pueden estar contribuyendo a su problema:

  1. Me doy count de que estás creando un object FMDatabaseQueue localmente. Solo debe tener un object FMDatabaseQueue compartido para toda la aplicación (lo pongo en un singleton). El propósito de la queue de la database es coordinar las interacciones de la database, y no puede hacerlo razonablemente si está creando nuevos objects FMDatabaseQueue todas partes.

  2. Le aconsejo que no tenga un bloque en la database en el que esté descargando sincrónicamente un montón de imágenes de la networking.

    Cuando envía una tarea inDatabase , cualquier llamada inDatabase a otros subprocesss que usan el mismo FMDatabaseQueue (y deben usar la misma queue, o si está derrotando el propósito de tener una queue en primer lugar) no avanzará hasta que la que esté en ejecución en su operación lo hace (o viceversa).

    Al hacer la interacción de la database desde múltiples subprocesss, coordinados por la queue de serie FMDatabaseQueue , realmente quiere asegurarse de "entrar y salir" lo más rápido posible. No incruste llamadas de networking potencialmente lentas en el medio del bloque inDatabase , o de lo contrario, toda la interacción de la database se bloqueará hasta que finalice.

    Entonces, haga una inDatabase para identificar las imágenes que necesitan ser descargadas, pero eso es todo. Luego, fuera de la llamada a la database, recupere sus imágenes, y si necesita actualizar las routes de image o similares, inDatabase llamada por separado en la base de inDatabase para hacerlo. Pero no incluya nada lento y sincrónico dentro del bloque inDatabase .

  3. También noto que estás haciendo un SELECT en la tabla de task , manteniendo ese FMRecordSet abierto y luego intentando actualizar el mismo logging. Desea abrir su set de loggings, recuperar lo que necesita y cerrar ese set de loggings antes de intentar actualizar el mismo logging que recuperó en su set de loggings.

    Siempre cierre el FMResultSet antes de intentar executeUpdate que actualiza el mismo logging.

  4. Un poco no relacionado, pero podría sugerirle que considere include img_url e img_path en su instrucción SELECT original, de esa manera su matriz de inputs de dictionary ya tendrá todo lo que necesita y le ahorrará tener que hacer ese segundo SELECT .


Si se está preguntando cómo podría ser el singleton FMDatabaseQueue , puede tener un singleton DatabaseManager cuya interfaz se ve así:

 // DatabaseManager.h #import <Foundation/Foundation.h> #import "FMDatabase.h" #import "FMDatabaseQueue.h" #import "FMResultSet.h" @interface DatabaseManager : NSObject @property (nonatomic, strong) FMDatabaseQueue *queue; + (instancetype)shanetworkingManager; @end 

y la implementación podría verse así:

 // DatabaseManager.m #import "DatabaseManager.h" @implementation DatabaseManager + (instancetype)shanetworkingManager { static id shanetworkingMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shanetworkingMyManager = [[self alloc] init]; }); return shanetworkingMyManager; } - (id)init { self = [super init]; if (self) { _queue = [[FMDatabaseQueue alloc] initWithPath:[[NSUserDefaults standardUserDefaults] valueForKey:@"pathDB"]]; } return self; } @end 

Entonces, cualquier código que necesite interactuar con la database puede recuperar la queue así:

 FMDatabaseQueue *queue = [[DatabaseManager shanetworkingManager] queue];