不允许更改核心数据关系,但最终允许删除子项

Posted

技术标签:

【中文标题】不允许更改核心数据关系,但最终允许删除子项【英文标题】:disallow change to core data relationship, but eventually allow for delete of the child 【发布时间】:2016-01-06 18:27:28 【问题描述】:

我有一个包含两个实体的核心数据模型,“父”和“子”。 父母与孩子有一对多的关系,孩子与父母有一对一的关系。 一旦设置了父级,我想防止更改子级的父级关系。但是,应该允许删除孩子。

孩子的 setParent 长这样:

- (void)setParent:(Parent *)parent 
   if (self.parent) return;
   [self willChangeValueForKey:@"parent"];
   [self setPrimitiveValue:parent forKey:@"parent"];
   [self didChangeValueForKey:@"parent"];
  

现在,一旦设置了父级,这将阻止更改,但同时会阻止子级的删除,因为在删除期间 setParent 被重新访问两次以将父级设置为 nil。

在删除过程中第一次调用 setParent 时,self.isDeleted 为 true。所以我可以对这种情况做出反应。但是在删除过程中再次调用了 setParent,这一次 self.isDeleted 为假,我不知道如何知道是否有人试图编辑父关系或是否正在发生删除。

我正在使用 MagicalRecord 2.30,删除调用如下所示:

[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext)      

    [sut MR_inContext:localContext];
    [sut MR_deleteEntityInContext:localContext];
];

我到处寻找有关删除期间那些 setter 调用的一些信息,但没有运气。 因此,我们将不胜感激。

编辑 我将一些 NSLogs 放入我的代码中以记录正在发生的事情。删除实际上适用于此代码,但我不知道所有这些调用是关于什么的。以下是相关代码:

- (BOOL) MR_deleteEntityInContext:(NSManagedObjectContext *)context 
    NSLog(@"|");
    NSLog(@"|");
    NSLog(@"----------------------------------");
    NSLog(@"| ***** starting delete... ***** |");
    NSLog(@"----------------------------------");
    NSLog(@"|");
    NSLog(@"|");
    return [super MR_deleteEntityInContext:context];


- (void)setParent:(Parent *)parent 
    NSLog(@"|");
    NSLog(@"--------------------------------------------------------------------------");
    NSLog(@"setParent has been called with parameter <%p>", parent);
    NSLog(@"                          self.parent is: %p", self.parent);
    NSLog(@"self.isDeleted is: %hhd", self.isDeleted);
    NSLog(@"         self.moc: %@", self.managedObjectContext);
    NSLog(@"--------------------------------------------------------------------------");
    NSLog(@"|");
    if (!self.isDeleted && self.parent) return;
    [self willChangeValueForKey:@"parent"];
    [self setPrimitiveValue:parent forKey:@"parent"];
    [self didChangeValueForKey:@"parent"];

它产生以下输出:

 ----------------------------------
 | ***** starting delete... ***** |
 ----------------------------------
 |
 |
 |
 --------------------------------------------------------------------------
 setParent has been called with parameter <0x0>
                           self.parent is: 0x6080000a9cc0
 self.isDeleted is: 1
          self.moc: <NSManagedObjectContext: 0x6000001c05a0>
 --------------------------------------------------------------------------
 |
 |
 --------------------------------------------------------------------------
 setParent has been called with parameter <0x0>
                           self.parent is: 0x6080000aa080
 self.isDeleted is: 0
          self.moc: <NSManagedObjectContext: 0x6080001c0a50>
 --------------------------------------------------------------------------
 |
 |
 --------------------------------------------------------------------------
 setParent has been called with parameter <0x0>
                           self.parent is: 0x6080000aa080
 self.isDeleted is: 1
          self.moc: <NSManagedObjectContext: 0x6080001c0a50>
 --------------------------------------------------------------------------
 |

【问题讨论】:

如果在self.isDeleted 为真时将parent 设置为nil,您可以相当确定第二次调用是否不是删除的一部分并不重要。除非你没有同步更新你的模型,否则你就完蛋了。但无论如何你都搞砸了。 @Avi - 谢谢。所以我让setter调用从delete pass,并期望父级设置为nil。但事实并非如此。测试是这样进行的,但是那里发生了什么??? 设置值后记录self.parent。如果你看,你会看到 moc 在第一次和第二次调用之间发生了变化。我猜第一个是第二个的子上下文。在第三次调用后,moc 与第二次相同,isDeleted == 1,你应该会发现parent 为 nil。 @Avi - 你是对的。那么父母是零。在 delete 期间第二次调用 setter 并没有通过条件,因此没有设置任何东西,这似乎无关紧要。最后经过第三轮,child的parent属性为nil,parent的children属性为空。感谢您的帮助。 【参考方案1】:

如果@Avi 建议isDeletedtrue,您可以将parent 设置为nil

【讨论】:

【参考方案2】:

这是我发现的。我对上面的代码做了一点修改,也记录了反比关系。

- (void)setParent:(Parent *)parent 
    NSLog(@" ");
    NSLog(@"--------------------------------------------------------------------------");
    NSLog(@"| setParent has been called with parameter <%p>", parent);
    NSLog(@"|                                 self is: %p", self);
    NSLog(@"|                          self.parent is: %p", self.parent);
    NSLog(@"|                parent.children contains: %p", [Parent MR_findFirstInContext:self.managedObjectContext].children.allObjects.firstObject);
    NSLog(@"| self.isDeleted is: %hhd", self.isDeleted);
    NSLog(@" ");
    if (!self.isDeleted && self.parent) return;
    if (self.isDeleted) [self setPrimitiveValue:nil forKey:@"parent"];
    [self willChangeValueForKey:@"parent"];
    [self setPrimitiveValue:parent forKey:@"parent"];
    [self didChangeValueForKey:@"parent"];
    NSLog(@"|- after setting the parent ...");
    NSLog(@"|-                                self is: %p", self);
    NSLog(@"|-                         self.parent is: %p", self.parent);
    NSLog(@"|-               parent.children contains: %p", [Parent MR_findFirstInContext:self.managedObjectContext].children.allObjects.firstObject);
    NSLog(@"--------------------------------------------------------------------------");

当我删除孩子时,会记录以下内容:

----------------------------------
| ***** starting delete... ***** |
----------------------------------
|
|

--------------------------------------------------------------------------
| setParent has been called with parameter <0x0>
|                                 self is: 0x6000000a79e0
|                          self.parent is: 0x6000000a7a40
|                parent.children contains: 0x6000000a79e0
| self.isDeleted is: 1

|- after setting the parent ...
|-                                self is: 0x6000000a79e0
|-                         self.parent is: 0x0
|-               parent.children contains: 0x6000000a79e0
--------------------------------------------------------------------------

--------------------------------------------------------------------------
| setParent has been called with parameter <0x0>
|                                 self is: 0x6080000a8100
|                          self.parent is: 0x6080000a8160
|                parent.children contains: 0x0
| self.isDeleted is: 0


--------------------------------------------------------------------------
| setParent has been called with parameter <0x0>
|                                 self is: 0x6080000a8100
|                          self.parent is: 0x6080000a8160
|                parent.children contains: 0x0
| self.isDeleted is: 1

|- after setting the parent ...
|-                                self is: 0x6080000a8100
|-                         self.parent is: 0x0
|-               parent.children contains: 0x0
--------------------------------------------------------------------------

因此,在删除期间第一次和第三次调用 setter 时,isDeleted 为真。但是在第二次调用 setter 时,isDeleted 是错误的。 如果我此时测试反向关系,我发现它已经被删除了。那个测试对我来说已经足够了。它允许删除循环通过,同时允许在根本没有设置关系的情况下运行设置器。

【讨论】:

以上是关于不允许更改核心数据关系,但最终允许删除子项的主要内容,如果未能解决你的问题,请参考以下文章

在 NHibernate 中定义多对多关系以允许删除但避免重复记录的正确方法是啥

Symfony 4 + 奏鸣曲管理包 + 关系。表单正在删除子项,但不仅是 parentId

如何在核心数据关系真正改变之前观察它?

何时不使用核心数据关系

对许多实体和实体关系进行过滤的核心数据提取

iPhone核心数据可以缓存NSManagedObject吗?