Random EXC_BAD_ACCESS with persistentStoreCoordinator

From time to time, my application crashes before it is fully loaded right on the following line:

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

The full method this if state is in is as follows (I think this is pretty standard):

- (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;
}

Update

The failure occurs the second time the method is called -(NSPersistentStoreCoordinator *)persistentStoreCoordinator. CHANGE frist time it is apparent from the first test monitor:

- (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]];
    ...
    });
}

The second time (when sometimes a crash occurs when my DeviceLinker class checks the database for inactive links in my checkInactiveLinks method. This method is called when launched in applicationDidBecomeActive:

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

, , , , , persistentStoreCoordinator getter, __persistentStoreCoordinator ...

2 if :

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

3

. -[NSPersistentStoreCoordinator unlock]: message sent to deallocated instance 0x8916090. , .

+5
4

, . , , " " . .

- (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;
}
+19

GCD .

, :

concurrency; , , .

CoreData concurrency . ?

+1

, , getter . :

#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;
}
+1

I think that when you add a new permanent store, you should first lock the permanent store coordinator so that it does not interfere with other permanent stores.

The code below pschas a separate link to the newly created persistent storage coordinator. If I understand your purpose correctly, this part of the method should be dispatch_asynced.

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

All Articles