初始化托管对象而不插入托管对象上下文时,核心数据对象属性为零

Posted

技术标签:

【中文标题】初始化托管对象而不插入托管对象上下文时,核心数据对象属性为零【英文标题】:Core Data object properties are nil when Managed Object is initialized WITHOUT inserting into a Managed Object Context 【发布时间】:2016-05-18 19:09:17 【问题描述】:

在我的 Core Data 应用程序中,我使用 initWithEntity:insertIntoManagedObjectContext:nil 上下文初始化托管对象。然后我使用[managedObjectContext insertObject:object] 将对象插入到上下文中,如果/需要的话。

看来这就是insertObject: API 存在的原因。插入在创建期间未插入的托管对象。

但是当使用上述逻辑时,对象(稍后从 MOC 获取时)具有 nil 属性。对象上的字符串不会被持久化。

如果我将 initWithEntity:insertIntoManagedObjectContext:non-nil MOC 一起使用,则属性 会保持不变并且一切正常。在这种情况下,我不打电话给insertObject:

代码的两个变体如下。只需切换useNilContext 标志即可尝试每个。

// ...

BOOL useNilContext = YES;

[[XYZBackend sharedBackend].managedObjectContextPrivateQueue performBlock:^
    XYZObject *object = [XYZObject objectFromJSON:json useNilContext:useNilContext];

    if (useNilContext) 
        [[XYZBackend sharedBackend].managedObjectContextPrivateQueue insertObject:object];
    

    NSError *privateQueueError = nil;
    if (![[XYZBackend sharedBackend].managedObjectContextPrivateQueue save:&privateQueueError]) 
        NSLog(@"Error saving Core Data managed object context in private queue! %@", privateQueueError);
    
];

// ...


+ (instancetype)managedObjectFromJSON:(NSDictionary *)json useNilContext:(BOOL)useNilContext 
    __block XYZManagedObject *object = nil;

    [[XYZBackend sharedBackend].managedObjectContextPrivateQueue performBlockAndWait:^
        NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([self class]) inManagedObjectContext:[XYZBackend sharedBackend].managedObjectContextPrivateQueue];

        NSManagedObjectContext *context = [XYZBackend sharedBackend].managedObjectContextPrivateQueue;
        if (useNilContext) 
            context = nil;
        

        object = [[XYZManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:context];

        object.stringProperty = json[@"string"];
    ];

    return object;

稍后,当我从 Core Data 获取对象并检查它时,我会得到不同的行为,具体取决于对象最初是否插入到 MOC 中。如果 MOC 是nil,那么在获取之后对象的所有属性都是nil

initWithEntity:insertIntoManagedObjectContext:nil MOC 一起使用:

Printing description of object:
<XYZObject: 0x60b0000b80d0> (entity: XYZObject; id: 0xd000000000040000 <x-coredata://5F843602-C03B-4339-AC50-0F70FD1545C9/XYZObject/p1> ; data: 
    stringProperty = nil;
)

使用非零 MOC 初始化:

Printing description of object:
<XYZObject: 0x60b000054680> (entity: XYZObject; id: 0xd000000000040000 <x-coredata://72502855-7204-4485-828C-BC07C51F7FE2/XYZObject/p1> ; data: 
    stringProperty = "string";
)

如您所见,当对象最初未插入 MOC 时,获取后的属性为 nil。当它最初被插入到 MOC 中时,属性是非 nil 并且是持久的。

为什么这些属性没有持久化?我认为这两种代码策略应该是相同的。

是否必须采取另一个步骤来保持最初具有nil 托管对象上下文的托管对象的属性?

【问题讨论】:

您是否尝试过在设置对象 stringProperty 之前和之后检查它。它实际上是在两种情况下都设置的吗? 【参考方案1】:

不,没有额外的步骤。至于为什么不保存属性,您的代码中还有其他原因导致了问题。无论您提供上下文还是使用 nil 上下文但随后插入对象,分配新托管对象的工作方式都相同。

看起来您可能混淆了上下文,但很难确定。当您调用insert: 时,您使用[TDBackend sharedBackend].managedObjectContextPrivateQueue。但是当您保存更改时,您使用self.managedObjectContextPrivateQueue。同时,当useNilContextNO 时,您使用[XYZBackend sharedBackend].managedObjectContextPrivateQueue 创建托管对象。您正在与[ZYZBackend sharedBackend].managedObjectContextPrivateQueue 上的块调用同步。这是一段相当简单的代码中的四种不同的上下文。无法确定,但它给人的印象是您在某些时候使用了错误的上下文

【讨论】:

谢谢,这些是我为这个问题编写的上述伪代码中的拼写错误。已更正。应该只使用单个 [XYZBackend sharedBackend].managedObjectContextPrivateQueue 我的真实版本是使用单个私有上下文(和主上下文)。

以上是关于初始化托管对象而不插入托管对象上下文时,核心数据对象属性为零的主要内容,如果未能解决你的问题,请参考以下文章

核心数据获取...为啥在将托管对象插入上下文 A 并保存上下文 A 后,不使用上下文 B 获取托管对象?

核心数据查询在托管对象上下文中插入新对象

如何安全地删除未保存的托管对象?

核心数据:托管对象与上下文一起保存后如何撤消操作

在核心数据中保存到托管对象上下文时的 SIGABRT

撤消核心数据托管对象