ios 核心数据不持久保存对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18284613/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:46:29  来源:igfitidea点击:

Core Data not saving objects persistently

iosobjective-ccore-dataios6

提问by Sanchit Bareja

I'm new to Core Data and as such am not sure if I'm making a mistake. I've downloaded some data from a REST API and it successfully saves the JSONresponse to disk. I'm trying to process the data and save it persistently using Core Data.

我是 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!");
        }
    }];

I've successfully processed the JSONand added it to an NSManagedObjectContext. In the first line, it shows that I've successfully attempted to insert 2 objects.

我已成功处理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;
})
)}

When I attempted [managedObjectContext save:&error], it does so successfully and print out "saved all records" as expected. However, when I go to my application .sqlitefile and check for added objects, I realize that it hasn't added any objects to the db.

当我尝试时[managedObjectContext save:&error],它成功完成并按预期打印出“已保存的所有记录”。但是,当我转到我的应用程序.sqlite文件并检查添加的对象时,我意识到它没有向数据库添加任何对象。

On app relaunch, I print out a list of objects that are already in the database and it confirms that I've none saved yet.

在应用程序重新启动时,我打印出数据库中已经存在的对象列表,它确认我还没有保存任何对象。

Does anyone know what's going on and why I'm not able to save the data persistently even though it looks like I've successfully created the 'User' objects that needs to be saved in the Core Data model.

有谁知道发生了什么以及为什么我无法持久保存数据,即使看起来我已经成功创建了需要保存在核心数据模型中的“用户”对象。

EDIT:

编辑:

here is where I create the NSPersistentStoreCoordinator

这是我创建的地方 NSPersistentStoreCoordinator

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's 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's 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;
}

I have 3 contexts.

我有 3 个上下文。

  • masterManagedObjectContext

  • backgroundManagedObjectContext

  • newManagedObjectContext

  • masterManagedObjectContext

  • backgroundManagedObjectContext

  • newManagedObjectContext

master is parent of both background and new. When I query the contexts like this:

master 是 background 和 new 的父级。当我查询这样的上下文时:

    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 and background both return the correct obj.id in the NSLog as well as gives the output below for @"query records"

master 和 background 都在 NSLog 中返回正确的 obj.id 并为@“查询记录”提供下面的输出

   (
    "<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})"
)

however "new" returns (null)for the obj.id in NSLogand returns the following for @"query records":

但是“new”返回(null)obj.idNSLog并返回以下内容@"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>)"
)

回答by Mundi

From your code and the comments it seems that you are not saving the master context. Make sure you call

从您的代码和注释看来,您没有保存主上下文。确保你打电话

[managedObjectContext save:&error]; 

on all child contexts that save the data, and afterthat on the master context as well.

在所有保存数据的子上下文上,然后主上下文上也是如此。

回答by vaticRite

I just got done banging my head against essentially the same problem. A UITableViewController fetched a subclass of NSManagedObject from the NSManagedObjectContext, checked if an attribute was nil, and if it was downloaded the data, set that attribute, then saved the NSManagedObjectContext. Something like this:

我刚刚解决了基本相同的问题。UITableViewController 从 NSManagedObjectContext 获取 NSManagedObject 的子类,检查属性是否为零,如果下载了数据,设置该属性,然后保存 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

The save function was giving a YES boolean return and saveError was remaining nil, but if I quit the app and relaunched, when my Core Data loaded up my NSManagedObject subclasses, the data attribute was nil, and when this UITableViewController came back up, it had to download the data again.

save 函数返回 YES 布尔值,saveError 仍然为零,但是如果我退出应用程序并重新启动,当我的核心数据加载我的 NSManagedObject 子类时,数据属性为零,当这个 UITableViewController 出现时,它有再次下载数据。

I couldn't really find a solution to this anywhere… reading through the Core Data documentation didn't help. The solution came to me when I considered the difference between the above code and my code that sets the attributes in the NSManagedObject subclass's factory methods, which is basically:

我真的在任何地方都找不到解决方案……阅读 Core Data 文档并没有帮助。当我考虑到上面的代码和我在 NSManagedObject 子类的工厂方法中设置属性的代码之间的区别时,我想到了解决方案,这基本上是:

MyManagedObject *mgObject = [NSEntityForDescription insertNewObjectForEntityName:@"MyManagedObject" inManagedContext:mgObContext];
mgObject.attribute1 = some value
mgObject.attribute2 = another value

The only difference is that I'm calling the factory methods from inside a [mgObContext performBlock:].

唯一的区别是我从 [mgObContext performBlock:] 内部调用工厂方法。

So the amended code is:

所以修改后的代码是:

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

Which, thus far, is working perfectly. So I think anytime you made modifications to NSManagedObjects' attributes, you need to do so on the their NSManagedObjectContext's thread.

到目前为止,它工作得很好。所以我认为任何时候你对 NSManagedObjects 的属性进行了修改,你都需要在他们的 NSManagedObjectContext 的线程上这样做。

回答by Marcel

Figured I would also add some input to people who may have similar issues.

想我也会向可能有类似问题的人添加一些意见。

In my experience, attempting to save objects that don't have sufficient fields filled out don't seem to persist when saving, and no errors seem to be thrown when this is the case. Always double check that your fields are being filled in as expected before the save fires.

根据我的经验,尝试保存没有填写足够字段的对象在保存时似乎不会持续存在,并且在这种情况下似乎不会抛出任何错误。在保存触发之前,请务必仔细检查您的字段是否按预期填写。

Another way to look at these types of issues is to flip the problem on its head. Maybe the object did in fact save, but the method in which you're verifying that they have in fact been saved is wrong. Often you might do this by querying CoreData for the record(s) using certain criteria. Double check that your criteria is correct and that it your query is actually returning what you expect.

看待这些类型问题的另一种方法是将问题颠倒过来。也许该对象实际上确实保存了,但是您验证它们实际上已保存的方法是错误的。通常,您可以通过使用特定条件查询 CoreData 的记录来完成此操作。仔细检查您的标准是否正确,并且您的查询实际上返回了您期望的内容。

If it does not return what you expect, it could be due to your own errors, but it could also be that the array storing your results isn't storing them properly. I have run into cases before where I had to rename an NSArraybecause something about the array name was causing referencing issues, and thus the array could not point to the results I was expecting. Cheers.

如果它没有返回您期望的结果,可能是由于您自己的错误,但也可能是存储结果的数组没有正确存储它们。在我不得不重命名 an 之前,我遇到过NSArray一些情况,因为有关数组名称的某些内容导致了引用问题,因此数组无法指向我期望的结果。干杯。

回答by VMCuongOnStackOverflow

Add this after you save your data :

保存数据后添加:

NSError *error = nil;
if (![managedObjectContext save:&error]) {
    NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}

回答by Ernesto Fernandez

I know that this is not an answer to what the OP asked, but I wanted to share my experience about the same subject in case it will help someone else.

我知道这不是 OP 所问问题的答案,但我想分享我对同一主题的经验,以防它对其他人有所帮助。

I had some issues with saving data persistently, anything seemed to help me fix it. The structure was very simple, an Entity with one field and one relationship (to-many). I made some changes to the class generated, NSMutableOrderedSetinstead of NSOrderedSet.

我在持续保存数据时遇到了一些问题,似乎任何东西都可以帮助我解决它。结构非常简单,一个具有一个字段和一个关系(对多)的实体。我对生成的类进行了一些更改,NSMutableOrderedSet而不是NSOrderedSet.

I was not doing multi thread, or anything like that, just adding elements to the relationship. After saving, and re-launching the application, data just disappeared (elements added to the relationship).

我没有做多线程或类似的事情,只是在关系中添加元素。保存并重新启动应用程序后,数据就消失了(添加到关系中的元素)。

I ended up discovering that there is a property called updated. After adding the new element to the relationship, I checked if this property changed its value. It didn't. So I had to create another field in the Entity, a Boolean, just to be able to force the entity to be saved after adding elements to this relationship.

我最终发现有一个名为updated的属性。将新元素添加到关系后,我检查了此属性是否更改了其值。它没有。所以我必须在实体中创建另一个字段,一个布尔值,以便能够在向此关系添加元素后强制保存实体。

entity.addObject(..)
entity.forceUpdate = true // without this line, it won't update
managedContext.save(..)

So I hope it helps anyone with the same problem, as I spent some time thinking that I was not saving it correctly..

所以我希望它可以帮助任何有同样问题的人,因为我花了一些时间认为我没有正确保存它..

回答by EnriMR

I'm a beginner with iOS but I have done some example with CoreData to store users info.

我是 iOS 的初学者,但我用 CoreData 做了一些例子来存储用户信息。

First, you need to create your model with your entity (I suppose you have already done). In my example, my entity is called "User".

首先,您需要使用您的实体创建模型(我想您已经完成了)。在我的示例中,我的实体称为“用户”。

First, add a property similar to this

首先,添加一个类似于这个的属性

NSManagedObjectContext *context;

to your ViewController class.

到您的 ViewController 类。

Second, in your viewDidLoad method, add this two lines:

其次,在您的 viewDidLoad 方法中,添加以下两行:

AppDelegate *appdelegate = [[UIApplication sharedApplication]delegate];
context = [appdelegate managedObjectContext];

And third, store your info:

第三,存储您的信息:

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];

(I take my properties from a NSDictionary called dictionary)

(我从名为字典的 NSDictionary 中获取我的属性)

To read your info:

阅读您的信息:

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"];
    }
}

回答by Khon

After hours of debugging, I found that the reason my updates weren't being saved was because in my subclass of NSManagedObjectI defined by properties w/ @synthesizeinstead of @dynamic.

经过数小时的调试,我发现我的更新没有被保存的原因是因为在我的子类中,NSManagedObject我由属性 w/@synthesize而不是@dynamic.

After I change it, it all saved as expected.

我更改后,它都按预期保存了。

Hope that helped someone.

希望对某人有所帮助。

回答by Dhilip

If objects are missing where you dont have an inverse relationship, you need to save both the entities before mapping. Check this example, which I created to demonstrate how core data objects go missing and how to workaround, while working with Core data, for the case where you dont have an inverse relationship

如果在没有反向关系的地方缺少对象,则需要在映射之前保存两个实体。检查此示例,我创建该示例以演示在使用核心数据时核心数据对象如何丢失以及如何解决,如果您没有反向关系