Ответ 1
Из вашего кода и комментариев кажется, что вы не сохраняете главный контекст. Убедитесь, что вы вызываете
[managedObjectContext save:&error];
во всех дочерних контекстах, которые сохраняют данные, а затем и в главном контексте.
Я новичок в Core Data и поэтому не уверен, допустил ли я ошибку. Я загрузил некоторые данные из REST API и успешно сохраняет ответ JSON
на диск. Я пытаюсь обрабатывать данные и сохранять их настойчиво с помощью Core Data.
NSLog(@"inserted objects: %@", [managedObjectContext insertedObjects]);
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
if (![managedObjectContext save:&error]) {
NSLog(@"Unable to save context for class %@", className);
} else {
NSLog(@"saved all records!");
}
}];
Я успешно обработал JSON
и добавил его в NSManagedObjectContext
. В первой строке показано, что я успешно попытался вставить 2 объекта.
inserted objects: {(
<User: 0xa259af0> (entity: User; id: 0xa259b70 <x-coredata:///User/t44BB97D0-C4B4-4BA6-BD25-13CEFDAE665F3> ; data: {
email = "[email protected]";
experience = "2013-07-20";
"first_name" = Vishnu;
id = 2;
"job_title" = Developer;
"last_name" = Prem;
location = "";
"phone_number" = "+6590091516";
"profile_pic" = "";
"thumbnail_profile_pic" = "";
"user_id" = 2;
}),
<User: 0xa25e460> (entity: User; id: 0xa25e4c0 <x-coredata:///User/t44BB97D0-C4B4-4BA6-BD25-13CEFDAE665F2> ; data: {
email = "[email protected]";
experience = "2013-07-20";
"first_name" = Sanchit;
id = 1;
"job_title" = Developer;
"last_name" = Bareja;
location = "";
"phone_number" = "+15106127328";
"profile_pic" = "";
"thumbnail_profile_pic" = "";
"user_id" = 1;
})
)}
Когда я попытался [managedObjectContext save:&error]
, он делает это успешно и распечатывает "сохраненные все записи", как ожидалось. Однако, когда я перехожу к файлу приложения .sqlite
и проверяю наличие добавленных объектов, я понимаю, что он не добавил никаких объектов в db.
При повторном подключении приложения я распечатываю список объектов, которые уже находятся в базе данных, и подтверждает, что я еще не сохранил их.
Кто-нибудь знает, что происходит, и почему я не могу сохранить данные настойчиво, хотя похоже, что я успешно создал объекты User, которые необходимо сохранить в модели Core Data.
EDIT:
здесь я создаю NSPersistentStoreCoordinator
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"RTModel.sqlite"];
NSError *error = nil;
NSLog(@"Test 1");
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSLog(@"Test 2");
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() 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 persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
У меня 3 контекста.
masterManagedObjectContext
backgroundManagedObjectContext
newManagedObjectContext
master является родителем как фона, так и нового. Когда я запрашиваю контексты следующим образом:
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"User"];
[request setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:@"id" ascending:YES]]];
[request setReturnsObjectsAsFaults:NO];
NSArray *testArray = [[[RTCoreDataController sharedInstance] newManagedObjectContext] executeFetchRequest:request error:&error];
for (User *obj in testArray) {
NSLog(@"obj.id %@", obj.id);
}
NSLog(@"query records: %@",testArray);
master и background возвращают правильный obj.id в NSLog, а также выводят ниже для @ "записей запроса"
(
"<User: 0xa3811d0> (entity: User; id: 0xa381230 <x-coredata:///User/t92BCED2D-CD17-49CC-9EBA-DF8F52F06A002> ; data: {\n email = \"[email protected]\";\n experience = \"2013-07-20\";\n \"first_name\" = Sanchit;\n id = 1;\n \"job_title\" = Developer;\n \"last_name\" = Bareja;\n location = \"\";\n \"phone_number\" = \"+15106127328\";\n \"profile_pic\" = \"\";\n \"thumbnail_profile_pic\" = \"\";\n \"user_id\" = 1;\n})",
"<User: 0xa382170> (entity: User; id: 0xa3820b0 <x-coredata:///User/t92BCED2D-CD17-49CC-9EBA-DF8F52F06A003> ; data: {\n email = \"[email protected]\";\n experience = \"2013-07-20\";\n \"first_name\" = Vishnu;\n id = 2;\n \"job_title\" = Developer;\n \"last_name\" = Prem;\n location = \"\";\n \"phone_number\" = \"+6590091516\";\n \"profile_pic\" = \"\";\n \"thumbnail_profile_pic\" = \"\";\n \"user_id\" = 2;\n})"
)
однако "новый" возвращает (null)
для obj.id в NSLog
и возвращает для @"query records"
следующее:
(
"<User: 0xa2b08a0> (entity: User; id: 0x95aebe0 <x-coredata:///User/tBFCC6C5F-7D2C-4AA0-BA96-B806EE360A762> ; data: <fault>)",
"<User: 0xa2b0910> (entity: User; id: 0xa4b9780 <x-coredata:///User/tBFCC6C5F-7D2C-4AA0-BA96-B806EE360A763> ; data: <fault>)"
)
Из вашего кода и комментариев кажется, что вы не сохраняете главный контекст. Убедитесь, что вы вызываете
[managedObjectContext save:&error];
во всех дочерних контекстах, которые сохраняют данные, а затем и в главном контексте.
Я только что закончил, ударяя головой по существу той же самой проблемой. UITableViewController извлекал подкласс NSManagedObject из NSManagedObjectContext, проверял, был ли атрибут нулевым, и если он был загружен данными, установите этот атрибут, а затем сохранили NSManagedObjectContext. Что-то вроде этого:
MyManagedObject *mgObject = //get object from NSFetchResultsController
NSManagedObjectContext *mgObContext = mgObject.managedObjectContext;
if (!mgObject.data)
{
mgObject.data = [NSData dataWithContentsOfURL:urlWithData];
[mgObContext performBlock ^{
NSError *saveError = nil;
BOOL saveResult = [mgObContext save:&saveError];
if (saveError || !saveResult)
{
NSLog(@"Save not successful..");
}
}];
}
//do something with myObject.data
Функция сохранения давала логический возврат YES, а saveError оставался нулевым, но если я покинул приложение и перезапустил, когда мои базовые данные загрузили мои подклассы NSManagedObject, атрибут данных был равен нулю, и когда этот UITableViewController вернется, он должен был снова загрузить данные.
Я не мог найти решение этого в любом месте... чтение документации Core Data не помогло. Решение пришло ко мне, когда я рассмотрел разницу между приведенным выше кодом и моим кодом, который устанавливает атрибуты в подклассах NSManagedObject factory, которые в основном:
MyManagedObject *mgObject = [NSEntityForDescription insertNewObjectForEntityName:@"MyManagedObject" inManagedContext:mgObContext];
mgObject.attribute1 = some value
mgObject.attribute2 = another value
Единственное отличие состоит в том, что я вызываю методы factory изнутри [mgObContext performBlock:].
Итак, измененный код:
MyManagedObject *mgObject = //get object from NSFetchResultsController
NSManagedObjectContext *mgObContext = mgObject.managedObjectContext;
if (!mgObject.data)
{
[mgObContext performBlock: ^{
mgObject.data = [NSData dataWithContentsOfURL:urlWithData];
NSError *saveError = nil;
BOOL saveResult = [mgObContext save:&saveError];
if (saveError || !saveResult)
{
NSLog(@"Save not successful..");
}
}];
}
//do something with myObject.data
Который, до сих пор, работает отлично. Поэтому я думаю, что в любое время, когда вы вносили изменения в атрибуты NSManagedObjects, вам нужно сделать это в потоке NSManagedObjectContext.
Полагаю, что я также добавлю некоторый вклад в людей, у которых могут быть подобные проблемы.
По моему опыту, попытка сохранить объекты, которые не заполняют достаточное количество полей, по-видимому, не сохраняется при сохранении, и при этом не возникает ошибок. Всегда проверяйте, что ваши поля заполняются так, как ожидалось, до сохранения огня.
Еще один способ взглянуть на эти типы проблем - перевернуть проблему с ног на голову. Возможно, объект действительно сохранил, но метод, в котором вы проверяете, что они были фактически сохранены, неверен. Часто вы можете сделать это, запросив CoreData для записи (ов), используя определенные критерии. Дважды проверьте, что ваши критерии верны и что ваш запрос действительно возвращает то, что вы ожидаете.
Если он не возвращает то, что вы ожидаете, это может быть связано с вашими собственными ошибками, но также может быть, что массив, хранящий ваши результаты, не сохраняет их правильно. Я столкнулся с ситуациями до того, как мне пришлось переименовать NSArray
, потому что что-то о имени массива вызывало проблемы со ссылками, и поэтому массив не мог указать на ожидаемые результаты. Приветствия.
Я новичок в iOS, но я сделал пример с CoreData для хранения информации о пользователях.
Сначала вам нужно создать свою модель с вашей сущностью (я полагаю, вы уже сделали). В моем примере моя сущность называется "Пользователь".
Сначала добавьте свойство, подобное этому
NSManagedObjectContext *context;
в класс ViewController.
Во-вторых, в вашем методе viewDidLoad добавьте две строки:
AppDelegate *appdelegate = [[UIApplication sharedApplication]delegate];
context = [appdelegate managedObjectContext];
И в-третьих, сохраните свою информацию:
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:@"User" inManagedObjectContext:context];
NSManagedObject *newUser = [[NSManagedObject alloc]initWithEntity:entitydesc insertIntoManagedObjectContext:context];
[newUser setValue:(NSString *)[dictionary objectForKey:@"name"] forKey:@"name"];
[newUser setValue:(NSString *)[dictionary objectForKey:@"surname"] forKey:@"surname"];
...
NSError *error;
[context save:&error];
(Я беру свои свойства из NSDictionary, называемого словарем)
Чтобы прочитать информацию:
AppDelegate *appdelegate = [[UIApplication sharedApplication]delegate];
context = [appdelegate managedObjectContext];
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:@"User" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
//NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NULL"];
[request setPredicate:nil];
NSError *error;
NSArray *matchingData = [context executeFetchRequest:request error:&error];
//NSArray *matchingData = [context executeFetchRequest:nil error:&error];
// If the user is not logged in previously
if (matchingData.count <=0 ){
//self.displaylabel.text = @"No person find";
} else {
// If the user is already logged in
for (NSManagedObject *obj in matchingData) {
AppDataModel *appDataModel=[AppDataModel getInstance];
appDataModel.appUserInfo = [User alloc];
appDataModel.appUserInfo.name = [obj valueForKey:@"name"];
appDataModel.appUserInfo.surname = [obj valueForKey:@"surname"];
}
}
Я знаю, что это не ответ на вопрос OP, но я хотел поделиться своим опытом по тому же вопросу, если он поможет кому-то другому.
У меня были некоторые проблемы с сохранением данных настойчиво, все, казалось, помогло мне исправить это. Структура была очень простой: сущность с одним полем и одно отношение (для многих). Я внес некоторые изменения в генерируемый класс NSMutableOrderedSet
вместо NSOrderedSet
.
Я не делал многопоточность, или что-то в этом роде, просто добавляя элементы в отношения. После сохранения и повторного запуска приложения данные просто исчезли (элементы добавлены в отношение).
В итоге я обнаружил, что есть свойство, называемое updated. После добавления нового элемента в отношение, я проверил, изменило ли это свойство его значение. Это не так. Поэтому мне пришлось создать другое поле в Entity, логическом, просто чтобы заставить объект быть сохраненным после добавления элементов в это отношение.
entity.addObject(..)
entity.forceUpdate = true // without this line, it won't update
managedContext.save(..)
Поэтому я надеюсь, что это поможет любому, у кого есть такая же проблема, поскольку я потратил некоторое время на то, что я не правильно его сохраняю.
Добавьте это после сохранения ваших данных:
NSError *error = nil;
if (![managedObjectContext save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}
После нескольких часов отладки я обнаружил, что причина, по которой мои обновления не сохранялись, была в том, что в моем подклассе NSManagedObject
я определялся свойствами w/@synthesize
вместо @dynamic
.
После того, как я его сменил, все было сохранено, как ожидалось.
Надеюсь, что кто-то помог.
Если объекты отсутствуют там, где у вас нет обратной связи, вам необходимо сохранить оба объекта перед отображением. Проверьте этот пример, который я создал для демонстрации как пропадают основные объекты данных и как обойти эту проблему при работе с основными данными, если у вас нет обратной зависимости