Ios 是否有一种通用方法来获取从核心数据关系中插入或删除的NSManagedObject集?

Ios 是否有一种通用方法来获取从核心数据关系中插入或删除的NSManagedObject集?,ios,core-data,nsmanagedobject,Ios,Core Data,Nsmanagedobject,例如,假设我有一个User托管对象,该对象与friends关系为UserUser多对多。 现在让我们假设对于某个用户a,2个用户x和d已经处于a的朋友关系中。但是: 我将用户b和c添加到friends 我将d从friends中删除(请注意,我没有从上下文中删除对象本身,只是将其从关系中删除) 在这一点上,我想以某种方式告诉大家,对于朋友关系: x没有任何更改,因为它始终存在 b和c在“插入集”中 d位于“已删除集”中 或者,对于关系朋友: b、c和d有变化,并且仍然能够辨别b和c的变化类型

例如,假设我有一个
User
托管对象,该对象与
friends
关系为UserUser多对多。

现在让我们假设对于某个用户
a
,2个用户
x
d
已经处于
a
朋友关系中。但是:

  • 我将用户
    b
    c
    添加到
    friends
  • 我将
    d
    friends
    中删除(请注意,我没有从上下文中删除对象本身,只是将其从关系中删除)
  • 在这一点上,我想以某种方式告诉大家,对于
    朋友
    关系:

    • x
      没有任何更改,因为它始终存在
    • b
      c
      在“插入集”中
    • d
      位于“已删除集”中
    或者,对于关系
    朋友

    • b
      c
      d
      有变化,并且仍然能够辨别
      b
      c
      的变化类型为“插入”,而
      d
      的变化类型为“删除”/“删除”

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

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

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

    希望这对您有所帮助。

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

    //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];
            }]];
    
    在这一点上,我有两个插入和删除数组(我知道,我在我的问题中说了集合…但现在它仍然有效)。 不过,我观察到的一个问题是,要使其正常工作,我需要首先从服务器检索数据,将其解析为托管对象,将这些托管对象置于适当的“默认”关系中,然后进行上下文保存。只有在上下文保存之后,
    changedvalue
    才会反映更改,例如删除或插入的对象(如果默认情况下未插入)


    现在真正的问题是上下文中的
    -save:
    保存所有对象。因此,例如,如果我有两个实体,每个实体都有关系(即两个单独的实体,每个实体有不同的关系,我的所有示例都适用于这两个实体),如果我阅读上述代码,然后发出POST和DELETE请求,然后保存上下文,以便在此时“提交”这些对象,尝试下一步对另一个实体及其关系执行相同操作将失败,因为上下文保存会清除所有对象的更改值。

    在整个过程中,您是否希望在任何时候保存更改?如果不是,当在关系中没有保存更改时,考虑删除<代码> d>代码>是否有意义?Oracle存储在内存中。但在后台,托管对象是从来自服务器的json表示实例化的,并提交回服务器。这就是为什么这个例子讨论x和d对象。这个想法是x来自服务器,所以如果它被从关系中删除,我需要在friends路由上为它发出一个DELETE,否则什么都没有。对于其他对象,b和c是新的,所以我会在同一端点上为它们发布帖子。最后,d在发布之前被插入和删除,因此它不应该包含在发布到朋友的路由中。对于coredata,我可以做一个[context save:]或者不做,如果它有助于了解更改,因为它对实体JSON映射和服务器请求没有影响。顺便说一句,是的,你是对的,d被删除是没有意义的,因为我把示例搞糟了,对不起。我会改正的。基本上假设x和d都是从关系开始的。在我提交时x没有变化,d在移除集中,因为它在那里,但不再是