Core data 多对多关系的核心数据删除规则

Core data 多对多关系的核心数据删除规则,core-data,nspredicate,object-model,Core Data,Nspredicate,Object Model,我有一个包含容器和项目实体的核心数据模型。容器中可以有零个或多个项目。项目必须至少属于一个容器(但可以位于多个容器中) 这些关系如下所示: Container: Relationship: items, Destination: Item, Inverse: itemContainers Optional, To-Many Relationship Delete Rule: Nullify Item: Relationship: itemContainers, Destinat

我有一个包含容器和项目实体的核心数据模型。容器中可以有零个或多个项目。项目必须至少属于一个容器(但可以位于多个容器中)

这些关系如下所示:

Container:
  Relationship: items, Destination: Item, Inverse: itemContainers
  Optional, To-Many Relationship
  Delete Rule: Nullify

Item:
  Relationship: itemContainers, Destination: Container, Inverse: items
  Not-Optional, To-Many Relationship
  Delete Rule: Cascade
删除容器时会出现问题。该容器中的项对象将被更新,但如果该项仅存在于一个容器中,则itemContainers属性是一个不包含任何对象的集。保存对象图失败,因为该空集违反了itemContainers的项非可选设置

当然,使用NSPredicate(如“itemContainers@count==0”)很容易找到带有空itemContainers的Item对象,但似乎应该有一种方法来配置模型以自动执行此操作


那么有没有更简单/更好的方法呢?

我知道它没有核心数据提供的配置选项那么干净,但是我部署了一些项目,其中
容器
对象在删除它的子
实体时循环,检查它们是否有0
项容器
(在“Container.m”内):


在我的应用程序中,我将项目的容器关系设置为可选,并通过“智能容器”访问那些无容器项目

如果您不想这样做,我怀疑您将不得不处理保存失败,并删除违反规则的对象


我越来越多地将我处理核心数据的方法改为防御性的方法:假设验证将失败,并准备好处理它。集成iCloud sync时变得更为重要。

我认为您不能在模型中指定此行为,但我可以验证容器的

 - (void)removeItemObject:(Item *)value
{...
if(![[value itemContainers]count])
  [context deleteObject:value];
...
}
我在上面尝试了一个类似的问题,但在一次删除几个“容器”时发现了问题(这在OS X 10.8.2上)。在保存托管对象上下文之前,容器不会从
[item itemContainers]
中删除,因此
count
保持在1以上,
item
永远不会被删除

我使用
-[NSManagedObject isDeleted]
NSManagedObject
上的分类方法提出了以下解决方案

文件
NSManagedObject+rjsnondeletedbjects.h

#import <CoreData/CoreData.h>

@interface NSManagedObject (RJSNondeletedObjects)

- (NSSet *)RJS_nondeletedObjectsForToManyKeyPath:(NSString *)keyPath;
- (BOOL)RJS_hasOtherNondeletedObjectsForToManyKeyPath:(NSString *)keyPath;

@end
#import "NSManagedObject+RJSNondeletedObjects.h"

@implementation NSManagedObject (RJSNondeletedObjects)

- (NSSet *)RJS_nondeletedObjectsForToManyKeyPath:(NSString *)keyPath
{
    NSSet * result = nil;

    id allObjectsForKeyPath = [self valueForKeyPath:keyPath];

    if ( ![allObjectsForKeyPath isKindOfClass:[NSSet class]] ) return result;

    result = [(NSSet *)allObjectsForKeyPath objectsPassingTest:^BOOL(id obj, BOOL *stop)
    {
        BOOL testResult = ![obj isDeleted];
        return testResult;
    }];

    return result;
}

- (BOOL)RJS_hasOtherNondeletedObjectsForToManyKeyPath:(NSString *)keyPath
{
    BOOL result = NO;

    // self will be in the set of nondeleted objects, assuming it's not deleted. So we need to adjust the test threshold accordingly.
    NSUInteger threshold = [self isDeleted] ? 0 : 1;
    NSSet * nondeletedObjects = [self RJS_nondeletedObjectsForToManyKeyPath:keyPath];
    result = ( [nondeletedObjects count] > threshold );

    return result;
}

@end
容器

...
#import "NSManagedObject+RJSNondeletedObjects.h"
...
- (void)prepareForDeletion
{
    NSSet *childItems = [self items];

    for (Item *item in childItems) {
        if ([item RJS_hasOtherNondeletedObjectsForToManyKeyPath:@"containers"]) {
            continue;
        }

        [managedObjectContext deleteObject:item];
    }
}

我喜欢这样做:

- (void)didChangeValueForKey:(NSString *)inKey withSetMutation:(NSKeyValueSetMutationKind)inMutationKind usingObjects:(NSSet *)inObjects
{
    [super didChangeValueForKey:inKey withSetMutation:inMutationKind usingObjects:inObjects];

    if ([inKey isEqualToString:@"YOURRELATIONSHIP"] && self.YOURRELATIONSHIP.count == 0) {
        [self.managedObjectContext deleteObject:self];
    }
}
- (void)didChangeValueForKey:(NSString *)inKey withSetMutation:(NSKeyValueSetMutationKind)inMutationKind usingObjects:(NSSet *)inObjects
{
    [super didChangeValueForKey:inKey withSetMutation:inMutationKind usingObjects:inObjects];

    if ([inKey isEqualToString:@"YOURRELATIONSHIP"] && self.YOURRELATIONSHIP.count == 0) {
        [self.managedObjectContext deleteObject:self];
    }
}