Основные данные: ошибка: Исключение было обнаружено во время обработки изменений Core Data
У меня была эта проблема сейчас в течение нескольких дней, и это действительно расстраивает. Я пересматривал свой код снова и снова, пробовал другую вещь и продолжал иметь ту же проблему. Что происходит только в 50% случаев, всегда. Это усложняет процесс.
Проблема,
Я разбираю данные из трех файлов csv в свои основные данные, из которых 2 из парсинга файлов всегда идут хорошо, но средний/второй файл - это то, где всегда происходит авария, это будет адрес этого файла и класс managedObjectContext для этого файла.
Сообщение об ошибке
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'
Итак, в моем классе FetchDatastrong > я пытаюсь решить проблему по-разному.
- Сначала я изменил свой CSV файл и ввел N/A во все поля/ячейки, которые были пустыми.
- Во-вторых, я делаю проверку в классе FetchDatastrong > , если это не имеет значения, сохраните N/A.
- В-третьих, на мой взгляд, контроллер, где я запускаю разбор данных, теперь я разделил три разных свойства для этих трех объектов в своих основных данных.
@property (неатомный, сильный) NSManagedObjectContext * managedObjectContext;
@property (неатомный, сильный) NSManagedObjectContext * managedObjectContextGI;
@property (неатомный, сильный) NSManagedObjectContext * managedObjectContextVA;
Это может быть немного сумасшедшим или что-то еще, но мне действительно нужно исправить это, пытаясь найти любое возможное решение или подход к этому, это всегда хорошо, я думаю.
ViewController для вызова функций для выполнения анализа.
//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];
}
});
}
Это мои функции Основные данные и т.д. Что я выполнил проверку, если она была пустой, сохраните N/A и т.д.. все мои свойства, которые находятся в моей сущности, строки
#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];
}
** Здесь происходит спасение! *
#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];
}
Хорошо, я разместил наиболее подходящий код, который я думаю о проблеме. Пожалуйста, если что-то еще нужно или подробности о проблеме дайте мне знать, и я предоставлю его.
Спасибо заранее!
Ответы
Ответ 1
Ну, вся проблема заключалась в создании NSManagedObjectContext
и всего в Main Thread
, а затем его доступе или использовании в Background Thread
.
Итак, я только что выполнил этот пост, и теперь все работает отлично и плавно.
Большое спасибо за комментарии, которые он действительно поставил меня в правильном направлении, и это было полностью то, что мне нужно было, чтобы найти проблему.
Спасибо!
В AppDelegate.h
+ (NSManagedObjectContext *)mainQueueContext;
+ (NSManagedObjectContext *)privateQueueContext;
Затем в моем 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];
}
И наконец, в моем ViewController, где я отправляю работу на задний план.
// 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!!!!
});
Ответ 2
Следующая строка сохранила мой день:
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
Вам просто нужно установить тип concurrency как закрытый. Таким образом, он может выполнять несколько операций с базой данных одновременно в частной очереди.
Ответ 3
Для людей, которые делают это в Swift 3:
var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
Ответ 4
В Swift 4 я использую этот шаблон:
// 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
}()
и updateContext для операций обновления.