NSFetchRequest的意外行为

时间:2012-04-17 20:47:23

标签: core-data

我在上下文中创建了几个实体,用于使用

将其保存在db中
AppCalendarEntity *appCalendar = [AppCalendarEntity getInstanceWithManagedDocument:manageDocument];

添加几个实体后,我执行流动提取请求

 NSFetchRequest *requestToSeeIfCalendarWithIdExist = [NSFetchRequest fetchRequestWithEntityName:@"AppCalendarEntity"];
   NSArray *result = [managedDocument.managedObjectContext executeFetchRequest:requestToSeeIfCalendarWithIdExist error:&InternalError] ;

它返回结果,包括我在上下文中使用第一个命令添加的实体,而不是已保存在数据库中的条目。我确保在此阶段文档状态为UIDocumentStateNormal。

当我将此行添加到已经打开的文档(UIDocumentStateNormal)时,它会返回预期的结果,即它从db获取结果以及尚未保存到db的内存上下文。

[managedDocument openWithCompletionHandler:^(BOOL success) 
     {
       NSFetchRequest *requestToSeeIfCalendarWithIdExist = [NSFetchRequest fetchRequestWithEntityName:@"AppCalendarEntity"];
   NSArray *result = [managedDocument.managedObjectContext executeFetchRequest:requestToSeeIfCalendarWithIdExist error:&InternalError] ;
}

我的问题是 1-我希望在两种情况下查询结果都相同。为什么在上述情况下并非如此。 2-对我来说,如果文档状态是UIDocumentStateNormal,我不应该在上下文中调用“openWithCompletionHandler”来打开文档。在这个特殊的场景中,它在NSFetchRequest中产生了什么不同,它在添加它之后给了我想要的结果。

如果我出错了,请告诉我

这是完整的代码

这是函数的完整代码

    + (void ) saveCalendarArrayInDbIfItAlreadyDoesNotExist : (NSArray*) appCalendarArray   managedDocument: (UIManagedDocument*) managedDocument completionBlock : ( void(^) (NSArray* ObjectSavedSuccesfully, NSError *InternalError)) handler
        {

        // i dont know why i have to do it :( if i dont add openWithCompletionHandler my query doesnt fetch result from db rather just do query in-memory context and not db
        [managedDocument openWithCompletionHandler:^(BOOL success) 
         {
            void (^completionHandler)(NSArray* , NSError* );
            completionHandler = [handler copy ];

            NSError *error = nil;
            NSMutableArray *array = [[NSMutableArray alloc] init];

            for (id appCalendar in appCalendarArray) {

                if([appCalendar isKindOfClass:[AppCalendarEntity class]])
                {
                    AppCalendarEntity *appCalendarEntity = (AppCalendarEntity*) appCalendar;
                    NSFetchRequest *requestToSeeIfCalendarWithIdExist = [NSFetchRequest fetchRequestWithEntityName:@"MyEntity"];
                    requestToSeeIfCalendarWithIdExist.predicate = [NSPredicate predicateWithFormat:@"identifier = %@", appCalendarEntity.identifier];
                    NSError *InternalError = nil; 
                   [requestToSeeIfCalendarWithIdExist setShouldRefreshRefetchedObjects:YES];
                    NSArray *result = [managedDocument.managedObjectContext executeFetchRequest:requestToSeeIfCalendarWithIdExist error:&InternalError] ;

                   // "result" is different when we encapsulate it in openWithCompletionHandler and when we don't…….MY PROBLEM

                   if(result == nil)
                    {

                         // return error
                    }

                    // 1 object always return that depict the in memory(context) object we created but not saved. I expect it should be zero because no object has yet been saved to database.. 
                    else if(result.count > 1)
                    {

                        [managedDocument.managedObjectContext deleteObject:appCalendar];
                    }
                    else
                    {
                        [array addObject:appCalendarEntity];
                    }
                }
                else
                {

                   // error handling
                }
            }

            if (error != nil)
            {
                 completionHandler (nil, error);
                 return;
            }

            // saving all the objects
             [ managedDocument updateChangeCount:UIDocumentChangeDone ];

    }

2 个答案:

答案 0 :(得分:1)

使用UIManagedDocument时,不要在MOC上调用save,因为它实现了自动保存。但是,需要告诉它应该在将来的某个时刻进行自动保存。

  1. 在该函数中摆脱对openWithCompletionHandler的调用(我知道它只是用于调试此问题)。

  2. 替换

    [managedDocument.managedObjectContext save:& InternalError]

  3. [managedDocument updateChangeCount:UIDocumentChangeDone];
    

    这将通知文档现在可以保存它。

    修改

    首先,我认为你应该摆脱调试黑客攻击。你可以添加NSLog或NSAssert,但其余的东西只是让你很难说出你想要的原因,并且混淆了真正的问题。

    其次,你的真正目标是什么?我可以看到方法的名称,我可以看到代码,但它们不匹配。

    这里有太多“残酷”,很难理解你的问题。我将重新发布您的代码,以及删除“开放”内容的编辑,并将问题注释为代码注释。

    希望这一改变能帮助您解决问题。

    // First, the method name seems to indicate that some objects will be added
    // to the database.  however, the only database work in this method is removal.
    // I don't get it.
    + (void ) saveCalendarArrayInDbIfItAlreadyDoesNotExist : (NSArray*) appCalendarArray   managedDocument: (UIManagedDocument*) managedDocument
        {
            NSError *error = nil;
            NSMutableArray *array = [[NSMutableArray alloc] init];
    
            for (id appCalendar in appCalendarArray) {
                if([appCalendar isKindOfClass:[AppCalendarEntity class]]) {
                    // OK, we are filtering the array of objects.  We are only interested in
                    // objects of type AppCalendarEntity, and are going to use its identity
                    // property to look for objects of type MyEntity.
                    // What is the relationship between AppCalendarEntity and MyEntity?
                    AppCalendarEntity *appCalendarEntity = (AppCalendarEntity*) appCalendar;
                    NSFetchRequest *requestToSeeIfCalendarWithIdExist = [NSFetchRequest fetchRequestWithEntityName:@"MyEntity"];
                    requestToSeeIfCalendarWithIdExist.predicate = [NSPredicate predicateWithFormat:@"identifier = %@", appCalendarEntity.identifier];
                    NSError *InternalError = nil; 
                   [requestToSeeIfCalendarWithIdExist setShouldRefreshRefetchedObjects:YES];
                    NSArray *result = [managedDocument.managedObjectContext executeFetchRequest:requestToSeeIfCalendarWithIdExist error:&InternalError];
    
                   // OK, now we just got a result from searching for a MyEntity, where
                   // its identifier is the same as the appCalendarEntity.
                   if(result == nil)
                   {
                       // return error
                   }
                    // 1 object always return that depict the in memory(context) object we created but not saved. I expect it should be zero because no object has yet been saved to database.. 
                    else if(result.count > 1)
                    {
                        // I am extremely confused by this code.  First, why are you
                        // checking for more than 1 object?  The method name indicates
                        // you are going to insert something.  Furthermore, you are only
                        // deleting one object.  How many do you expect?  Also, why are
                        // you deleting an appCalendar?  You were searching for a MyEntity.
                        // If an appCalendar is a MyEntity, then that's terrible naming.
                        // Furthermore, it would explain why you are finding it...
                        // because you create entities by inserting them in a MOC to
                        // begin with!
                        [managedDocument.managedObjectContext deleteObject:appCalendar];
                    }
                    else
                    {
                        // Even more confusion.  You are adding this object to an internal
                        // array, not the database.  Furthermore, you are doing it if there
                        // are either 0 or 1 MyEntity objects in the database with matching
                        // identifier.
                        [array addObject:appCalendarEntity];
                    }
                }
            }
    
            // saving all the objects
            // OK - but the only thing being saved are the ones you deleted...
            [ managedDocument updateChangeCount:UIDocumentChangeDone ];
    }
    

    最后,如果我的预感是正确的,并且日历对象实际上是MyEntity对象,那么它们已经在MOC中 - 因为它们是如何创建的。当您进行提取时,您可以强制搜索忽略待处理的更改(如我之前的一条评论中所述),并且只接受保存的更改。

    如果您想忽略挂起的更改,

    fetchRequest.includesPendingChanges = NO;
    

答案 1 :(得分:0)

@Jody问题已经解决,感谢您抽出时间来解决这个问题。

首先让我解决你的困惑

1-是功能旨在保存在数据库中,它是一个有用的功能。传递给此函数的参数“appCalendarArray”由已在上下文中创建的实体组成。我有意删除了逻辑,因为它涉及与外部apis通信,解析json等等。在上下文中插入实体所需的代码已经包含在问题的第一部分中。

AppCalendarEntity *appCalendar = [AppCalendarEntity getInstanceWithManagedDocument:manageDocument];

2-我根据数据库中应该唯一的列,从已构建但尚未从上下文中保存的上下文中删除实体。如果我们已经在数据库中有对象的标识符,我们不想重新保存它。所以,我只是从上下文中删除它。此功能按预期工作,实体不会重新保存在数据库中。最后一行确实保存了上下文中留下的对象(如果有的话)。大部分时间都有很多。

3-对于错误输入抱歉AppCalendarEntity和MyEntity是相同的。

解决方案


我添加了这个标志fetchRequest.includesPendingChanges = NO; ,删除db,重新启动Xcode,它开始工作。谢谢你的坚持