2012-04-30 16 views
5

De vez en cuando mi aplicación se bloquea antes de que sea correcta carga completa en la siguiente línea:aleatoria EXC_BAD_ACCESS con persistentStoreCoordinator

if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) 

El método completo en esto si el estado se encuentra miradas de la siguiente manera (creo que esto es bastante estándar):

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 
{ 
    if (__persistentStoreCoordinator != nil) { 
     return __persistentStoreCoordinator; 
    } 

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreData.sqlite"]; 

    NSError *error = nil; 
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    }  

    return __persistentStoreCoordinator; 
} 

actualización

el accidente ocurre en el segundo tiempo de laSe llama al método. EDITAR El tiempo Frist se llama desde el primer viewController que es visible:

- (void)updateStats { 
    NSLog(@"Updating stats"); 
    dispatch_queue_t request_queue = dispatch_queue_create("updateNumberOfSchedules", NULL); 
    dispatch_async(request_queue, ^{ 
     AppDelegate *theDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; 
    [context setPersistentStoreCoordinator:[theDelegate persistentStoreCoordinator]]; 
    ... 
    }); 
} 

La segunda vez (cuando el accidente ocurre a veces cuando mi clase DeviceLinker va a comprobar la base de datos para enlaces inactivos en mis checkInactiveLinks . método Este método es llamado lanzamiento en applicationDidBecomeActive:

-(void) checkInactiveLinks { 
    AppDelegate *theDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] init]; 
    [newMoc setPersistentStoreCoordinator:[theDelegate persistentStoreCoordinator]]; 
    ... 
} 

corrígeme si me equivoco, pero la lectura de mi código me gustaría pensar que en el segundo tiempo, el captador persistentStoreCoordinator se llama debe devolver __persistentStoreCoordina Tor y no asigna e init una nueva ...

ACTUALIZACIÓN 2 En la misma línea exacta en la sentencia if me sale esto de vez en cuando también:

-[__NSCFDictionary _hasPrecomputedKeyOrder]: unrecognized selector sent to instance 0x7dd3770 

ACTUALIZACIÓN 3

Edité mi esquema de compilación y encendí zombis y registré excepciones en la pestaña de diagnóstico. Ahora veo -[NSPersistentStoreCoordinator unlock]: message sent to deallocated instance 0x8916090. Tenga en cuenta que no tengo ningún bloqueo explícito en mi código.

+0

¿Estás utilizando ARC? – mydogisbox

+0

¿Qué hay en los registros? –

+0

¿Está editando el modelo sin volver a crear una nueva versión del modelo primero, por casualidad? –

Respuesta

19

Parece que está obteniendo ese código de varios subprocesos al mismo tiempo. Cuando realiza la creación de instancias vagas, debe asegurarse de que la "primera vez" a través de un solo subproceso de ejecución pase al mismo tiempo. Puede usar la siguiente estrategia para sincronizar el acceso a ella en el hilo principal.

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 
{ 
    if (__persistentStoreCoordinator != nil) { 
     return __persistentStoreCoordinator; 
    } 

    // Add this block of code. Basically, it forces all threads that reach this 
    // code to be processed in an ordered manner on the main thread. The first 
    // one will initialize the data, and the rest will just return with that 
    // data. However, it ensures the creation is not attempted multiple times. 
    if (![NSThread currentThread].isMainThread) { 
     dispatch_sync(dispatch_get_main_queue(), ^{ 
      (void)[self persistentStoreCoordinator]; 
     }); 
     return __persistentStoreCoordinator; 
    } 

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreData.sqlite"]; 

    NSError *error = nil; 
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    }  

    return __persistentStoreCoordinator; 
} 
+2

Una línea se puede simplificar un poquito: 'if (! [NSThread isMainThread]) {' –

+0

thnx buddy. Eres genial. –

+0

Una razón más para tener mucho cuidado con la creación de instancias perezosas. Encuentro que hago instancias vagas como una optimización prematura, es hora de repensar eso. – Troy

1

Estoy usando GCD para usar el enhebrado.

Y:

De vez en cuando mi aplicación se bloquea antes de que sea adecuado a plena carga en la siguiente línea:

Suena como los síntomas clásicos de un error relacionado con la concurrencia; intermitente y aparentemente en el lugar equivocado.

CoreData tiene muy specific concurrency requirements. ¿Los conoces?

+0

Sí, leí esa página web exacta cuando comencé con CoreData y ahora hace unas horas y sigo cumpliendo con sus requisitos. Principalmente, asegúrese de utilizar un nuevo MOC en cada hilo y puede compartir persistentStoreCoordinators. – Pieter

+0

OK - solo comprobando. La concurrencia es algo complicado. Sin embargo, aún puede haber un problema de concurrencia. – bbum

0

Creo que cuando agrega una nueva tienda persistente, primero debe bloquear el coordinador de tienda persistente para que no interfiera con otras tiendas persistentes.

En el código siguiente psc es una referencia por separado al coordinador de tienda persistente recién creado. Si entiendo tu propósito correctamente, esta parte del método debería ser dispatch_async ed.

[psc lock]; 
if (![__persistentStoreCoordinator 
    addPersistentStoreWithType:NSSQLiteStoreType 
       configuration:nil 
          URL:storeURL 
         options:nil error:&error]) { 
    // deal with the error 
} 
[psc unlock]; 
+0

Gracias, pero no quiero una nueva psc ... Ese es el punto clave, quiero nuevos MOC en mis hilos, pero comparto el PSC, porque es fácil y es la manera recomendada por Apple. – Pieter

+0

No es una nueva psc, solo una referencia a la psc existente. Este código es de hecho de Apple (donde demostrar iCloud también se debe agregar una tienda persistente de forma asincrónica a una psc). – Mundi

+0

Lo intenté, pero parece que no solucionó el problema. – Pieter

1

Prefiero el bloqueo de giro, es un enfoque más claro que llamar al método getter recursivamente en el hilo principal. Aquí está mi solución:

#import <libkern/OSAtomic.h> 

@implementation MyClass { 
    OSSpinLock _lock; 
} 

- (instancetype)init { 
    self = [super init]; 
    if (self){ 
     _lock = OS_SPINLOCK_INIT; 
    } 
} 


- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 
{ 
    if (__persistentStoreCoordinator != nil) { 
     return __persistentStoreCoordinator; 
    } 

    // Lock the current thread 
    OSSpinLockLock(&_lock); 

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreData.sqlite"]; 

    NSError *error = nil; 
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    } 

    // Unlock 
    OSSpinLockUnlock(&_lock); 

    return __persistentStoreCoordinator; 
} 
+0

¿Cuál es la ventaja de usar este método? – Politta

+1

@Politta Personalmente, para mí es más claro, el código anterior garantiza que todo lo que se encuentre entre 'OSSpinLockLock (& ​​_ lock);' y 'OSSpinLockUnlock (& ​​_ lock);' solo será accedido por un hilo. Si otro hilo intenta acceder a él, tiene que esperar el primero para completar la ejecución (para alcanzar 'OSSpinLockUnlock (& ​​_ lock);'). Aquí hay un poco de documentación de 'OSSpinLockLock' https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/OSSpinLockLock.3.html Espero que haya sido útil. Aclamaciones –

Cuestiones relacionadas