Core Data: error: Exception was caught during Core Data change processing

╄→гoц情女王★ 提交于 2020-02-26 05:13:33

问题


I have been having this problem now for a few days and it's really frustrating.. I have been reviewing my code over and over again tried different thing and keeps having the same issue.. Which happens only 50% of the times not always. This makes it harder..

The Problem,

I am parsing the data from 3 csv files to my Core Data, which 2 of the files parsing always goes well but the middle/second file is where the crash always happens so, this will be address to that file and managedObjectContext class for this file.

Error Message

CoreData: error: Serious application error.  
Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  
-[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
2014-09-12 11:27:06.115 AppName[210:3907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'

So, in my FetchData class I try to address the problem in different ways.

  • First, I changed my .csv file and insert N/A to all the fields/cells that were empty.
  • Second, I'm doing a check in my FetchData class if this doesn't has any value, save N/A.
  • Third, In my view controller where I'm firing the parsing the data, I have now separated three different properties for these 3 entities in my Core Data.

@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;

@property (nonatomic, strong) NSManagedObjectContext *managedObjectContextGI;

@property (nonatomic, strong) NSManagedObjectContext *managedObjectContextVA;

It might be a bit crazy or whatever but I really need to fix this so, trying any possible solution or approach to this it's always good, I think.

ViewController to calling the functions to perform the parsing..

//at the beginning of my model
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

-(IBAction)myLoadingTask:(id)sender{

       dispatch_async(kBgQueue, ^{

           NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

           NSString *savedValue = @"";

           if([[userDefaults stringForKey:@"dataFetched"] length] > 0){                            
               savedValue = [userDefaults stringForKey:@"dataFetched"];
           }

           // if the csv files data hasn't been fetch it, then fetch it
           if([savedValue length] == 0){

               FetchData *fd = [[FetchData alloc] initWithManagedContext:self.managedObjectContext];

               // fetching benefits data
               [fd beginParser];

               FetchGIBillData *fdGI = [[FetchGIBillData alloc] initWithManagedContext:self.managedObjectContextGI];

               // fetching gi bill data
               [fdGI beginParser];

               FetchVAPhones *fdVA = [[FetchVAPhones alloc] initWithManagedContext:self.managedObjectContextVA];

               // fetching va phones
               [fdVA beginParser];

               NSString *valueToSave = @"saved";
               [[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:@"dataFetched"];
               [[NSUserDefaults standardUserDefaults] synchronize];

           }

       });
}

This is my Core Data model functions and so on.. Which I have performed the check if it was empty save N/A and so on.. all my properties which are in my entity are strings

#define GIBILL_FILENAME @"gi_bill_data"

int numOfEntries;
- (id)initWithManagedContext:(NSManagedObjectContext*)managedObjectContext
{
    self.managedObjectContext = managedObjectContext;
    arrayOfRecords = [[NSMutableArray alloc] init];
    numOfEntries=0;
    return self;
}


- (void) beginParser
{
    if (self.managedObjectContext == nil){
        // Error: Must pass in NSManagedObjectContext
        return;
    }

    NSString *filePath = [[NSBundle mainBundle] pathForResource:GIBILL_FILENAME ofType:@"csv"];
    NSInputStream *stream = [NSInputStream inputStreamWithFileAtPath:filePath];
    NSStringEncoding encoding = NSUTF8StringEncoding;//NSWindowsCP1250StringEncoding;

    CHCSVParser *parser = [[CHCSVParser alloc] initWithInputStream:stream usedEncoding:&encoding delimiter:','];
    parser.delegate = self;

    [parser parse];

    // uncomment to update data x amount of dates
    //[self checkDateForRefreshCSV:parser];

}

** This is where the saving happens!! *

#pragma mark - Data Add
/**
 * addRows
 * @param parser: the CHCSV parser that will parse if required refresh
 * @brief: add the row to ths managedObjectContent DB. All values saved.
 */
- (void) addRows:(CHCSVParser *)parser
{
    int i = -1;
    if ([arrayOfRecords count] == 0) return;
    GIBill *data = [NSEntityDescription
                      insertNewObjectForEntityForName:@"GIBill"
                      inManagedObjectContext:self.managedObjectContext];

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.facility_code = [arrayOfRecords objectAtIndex:i];
    else
        data.facility_code = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.institution = [arrayOfRecords objectAtIndex:i];
    else
        data.institution = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.city = [arrayOfRecords objectAtIndex:i];
    else
        data.city = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.state = [arrayOfRecords objectAtIndex:i];
    else
        data.state = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.country = [arrayOfRecords objectAtIndex:i];
    else
        data.country = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.bah = [arrayOfRecords objectAtIndex:i];
    else
        data.bah = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.poe = [arrayOfRecords objectAtIndex:i];
    else
        data.poe = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.yr = [arrayOfRecords objectAtIndex:i];
    else
        data.yr = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.gibill = [arrayOfRecords objectAtIndex:i];
    else
        data.gibill = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 0)
        data.cross = [arrayOfRecords objectAtIndex:i];
    else
        data.cross = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.grad_rate = [arrayOfRecords objectAtIndex:i];
    else
        data.grad_rate = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.grad_rate_rank = [arrayOfRecords objectAtIndex:i];
    else
        data.grad_rate_rank = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.default_rate = [arrayOfRecords objectAtIndex:i];
    else
        data.default_rate = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.avg_stu_loan_debt = [arrayOfRecords objectAtIndex:i];
    else
        data.avg_stu_loan_debt = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.avg_stu_loan_debt_rank = [arrayOfRecords objectAtIndex:i];
    else
        data.avg_stu_loan_debt_rank = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.indicator_group = [arrayOfRecords objectAtIndex:i];
    else
        data.indicator_group = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.salary = [arrayOfRecords objectAtIndex:i];
    else
        data.salary = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.zip = [arrayOfRecords objectAtIndex:i];
    else
        data.zip = @"N/A";

    if([[arrayOfRecords objectAtIndex:++i] length] > 2)
        data.ope = [arrayOfRecords objectAtIndex:i];
    else
        data.ope = @"N/A";

    NSError *error;

    [self.managedObjectContext save:&error];

}

Well, I posted the most relevant code that I think of the issue. Please if something else is needed or more details about the problem let me know and I will provide it..

Thanks in advance!


回答1:


The line below saved my day:

_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

You just have to set the concurrency type as Private. This way it can perform multiple operations on database concurrently on private queue's.




回答2:


Well, the whole problem was creating the NSManagedObjectContext and everything in the Main Thread and then accessing it or using it in the Background Thread.

So, I have just followed this post and now everything is working perfectly and smooth :)

Thanks a lot for the comments it really put me in the right direction and it was totally what I needed to be able to find the problem..

Thanks!

In the AppDelegate.h

+ (NSManagedObjectContext *)mainQueueContext;
+ (NSManagedObjectContext *)privateQueueContext;

Then, in my AppDelegate.m

#pragma mark - Singleton Access

+ (NSManagedObjectContext *)mainQueueContext
{
    return [self mainQueueContext];
}

+ (NSManagedObjectContext *)privateQueueContext
{
    return [self privateQueueContext];
}

#pragma mark - Getters

- (NSManagedObjectContext *)mainQueueContext
{
    if (!_mainQueueContext) {
        _mainQueueContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        _mainQueueContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
    }

    return _mainQueueContext;
}

- (NSManagedObjectContext *)privateQueueContext
{
    if (!_privateQueueContext) {
        _privateQueueContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        _privateQueueContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
    }

    return _privateQueueContext;
}

- (id)init
{
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(contextDidSavePrivateQueueContext:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:[self privateQueueContext]];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(contextDidSaveMainQueueContext:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:[self mainQueueContext]];
    }
    return self;
}

#pragma mark - Notifications

- (void)contextDidSavePrivateQueueContext:(NSNotification *)notification
{
    @synchronized(self) {
        [self.mainQueueContext performBlock:^{
            [self.mainQueueContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
}

- (void)contextDidSaveMainQueueContext:(NSNotification *)notification
{
    @synchronized(self) {
        [self.privateQueueContext performBlock:^{
            [self.privateQueueContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

And, finally in my ViewController where I am sending the work to the background..

// dont forget the macro
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

dispatch_async(kBgQueue, ^{

    id delegate = [[UIApplication sharedApplication] delegate];
    self.managedObjectContext = [delegate privateQueueContext];

    // do something in the background with your managedObjectContext!!!!

 });



回答3:


For people who are doing it in Swift 3:

var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)



回答4:


In Swift 4 I use this template:

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
    /*
     The persistent container for the application. This implementation
     creates and returns a container, having loaded the store for the
     application to it. This property is optional since there are legitimate
     error conditions that could cause the creation of the store to fail.
     */
    let container = NSPersistentContainer(name: "Model")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

            /*
             Typical reasons for an error here include:
             * The parent directory does not exist, cannot be created, or disallows writing.
             * The persistent store is not accessible, due to permissions or data protection when the device is locked.
             * The device is out of space.
             * The store could not be migrated to the current model version.
             Check the error message to determine what the actual problem was.
             */
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

lazy var viewContext: NSManagedObjectContext = {
    return self.persistentContainer.viewContext
}()

lazy var cacheContext: NSManagedObjectContext = {
    return self.persistentContainer.newBackgroundContext()
}()

lazy var updateContext: NSManagedObjectContext = {
    let _updateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    _updateContext.parent = self.viewContext
    return _updateContext
}()

and updateContext for update operations.



来源:https://stackoverflow.com/questions/25812268/core-data-error-exception-was-caught-during-core-data-change-processing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!