是否有一种通用的方法来获取从Core Data关系中插入或删除的一组NSManagedObjects?

时间:2013-04-25 19:57:16

标签: ios core-data nsmanagedobject

例如,假设我有一个User托管对象与friends关系用户< - >用户多对多。

现在假设有些用户a,2位用户xd已经处于a的{​​{1}}关系中。但那时:

  1. 我将用户friendb添加到c
  2. 我从friends删除了d(请注意,我不会从上下文中删除对象本身,只需将其从关系中删除)。
  3. 此时,我想以某种方式告诉friends关系:

    • friends没有任何变化,因为它始终存在。
    • xb位于“插入的集合”中。
    • c位于“已移除的内容”中。

    或者,对于关系d

    • friendsbc有更改,仍然可以告诉db的更改是“插入”类型而c的类型为“删除”/“删除”

    我还没有找到实现这一目标的通用方法。上下文已更新,插入和删除了集,但这是针对上下文的,而不是针对特定关系。

2 个答案:

答案 0 :(得分:0)

NSManagedObject可以通过调用- (NSDictionary *)changedValues为您提供有关其更改的更多信息。但遗憾的是,仅仅跟踪关系变化还不够。下面,我分享了一种我之前使用的技术。您应该在执行save:之前找到更改。

NSDictionary* relationshipsByName = [[object entity] relationshipsByName];
NSDictionary* changedValues = [object changedValues];
NSManagedObjectContext* contextToCompateChanges = [[NSManagedObjectContext alloc] init];
[contextToCompateChanges setPersistentStoreCoordinator:persistentStoreCoordinator];

for (NSString* propertyName in changedValues) {
    NSRelationshipDescription* relationshipDescription = [relationshipsByName objectForKey:propertyName];

    if (relationshipDescription == nil]) {
        continue;
    }

    if ([relationshipDescription isToMany]) {
        NSSet* changedValue = [changedValues objectForKey:propertyName];
        NSManagedObject* oldObject = [contextToCompateChanges objectWithID:[object objectID]];
        NSSet* oldValue = [oldObject valueForKey:propertyName];

        NSExpression* expr = [NSExpression expressionForKeyPath:@"objectID"];
        NSSet* changedIDs = [expr expressionValueWithObject:changedValue context:nil];
        NSSet* oldIDs = [expr expressionValueWithObject:oldValue context:nil];

        NSMutableSet* insertedSet = [NSMutableSet setWithSet:changedIDs];
        [insertedSet minusSet:oldIDs]];
        // insertedSet will contain objectsIDs of newly inserted objects

        NSMutableSet* deletedSet = [NSMutableSet setWithSet:oldIDs];
        [deletedSet minusSet:changedIDs];
        // deletedSet will contain objectsIDs of deleted objects
    } else {
        NSManagedObject* newRelationshipValue = [changedValues objectForKey:propertyName];
        if ((NSNull*)newRelationshipValue == [NSNull null]) {
            NSManagedObject* oldObject = [contextToCompateChanges objectWithID:[object objectID]];
            newRelationshipValue = [oldObject valueForKey:propertyName];

            if (newRelationshipValue == nil) {
                continue;
            }
            // newRelationshipValue is object that was deleted from relationsip
        } else {
            // newRelationshipValue is object that was inserted into relationsip
        }
    }       
}

[contextToCompateChanges release];

希望这对你有所帮助。

答案 1 :(得分:0)

到目前为止,我提出了以下解决方案:

//Assume context, entityName, nameOfRelationship,etc... to be properly defined

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:entityName];
        NSArray *fetchResult = [self.context executeFetchRequest:fetchRequest error:&error];

        NSArray *deletedObjects = [fetchResult filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
            id relationship = [evaluatedObject changedValues][nameOfRelationship];
            return relationship && ![relationship containsObject:self.currentUser];
        }]];

        NSArray *insertedObjects = [fetchResult filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
            id relationship = [evaluatedObject changedValues][nameOfRelationship];
            return relationship && [relationship containsObject:self.currentUser];
        }]];

此时我有2个插入和删除数组(我知道,我在我的问题中说过设置......但现在这个有效)。 我观察到的一个问题是,为了使其工作,我需要首先从服务器检索数据,将其解析为托管对象,将这些托管对象放在适当的“默认”关系中,然后进行上下文保存。只有在上下文保存后,changedValues才会反映更改,例如正在删除或插入的对象(如果默认情况下未插入)。

现在真正的问题是上下文中的-save:保存了所有对象。所以,例如,如果我有两个实体,每个实体都有关系(即两个独立的实体,每个实体都有不同的关系,我的所有示例都适用于两个实体),如果我查看上面的代码,然后发出POST和DELETE请求,然后保存上下文,以便在那时这些对象被“提交”,尝试接下来对另一个实体执行相同操作,并且它的关系将失败,因为上下文保存会清除所有对象的changedValues。