Objective c NSFetchedResultsController在保存列出的一个对象后损坏了排序

Objective c NSFetchedResultsController在保存列出的一个对象后损坏了排序,objective-c,ios,nsfetchedresultscontroller,Objective C,Ios,Nsfetchedresultscontroller,我有一组用于CoreData浏览的通用视图/类,但在保存对所列对象之一的属性的更改后,我的“获取结果”控制器的排序顺序出现问题 在视图中将出现:我的表视图控制器中,我设置了“获取结果”控制器,如下所示: - (void) setupFetchedResultsController { NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName: self.entityToList]; request.p

我有一组用于CoreData浏览的通用视图/类,但在保存对所列对象之一的属性的更改后,我的“获取结果”控制器的排序顺序出现问题

视图中将出现:
我的表视图控制器中,我设置了“获取结果”控制器,如下所示:

- (void) setupFetchedResultsController {
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName: self.entityToList];
    request.predicate = self.entitySelectionPredicate; // Typically nil
    request.sortDescriptors = self.entitySortDescriptorList;
    self.fetchedResultsController = [[NSFetchedResultsController alloc]
                 initWithFetchRequest: request 
                 managedObjectContext: self.contextForEntity
                 sectionNameKeyPath: self.keyPathForSections
                 cacheName: nil]; /* Not chacheing */
}
didselectRowatingIndexPath
中,我将此tableview控制器推送到一个细节表格视图控制器,如下所示:

- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
    id objectInCell = [self.fetchedResultsController objectAtIndexPath: indexPath];
    ManagedObjectDetailTableViewController *dvc = [[ManagedObjectDetailTableViewController alloc]
                    initWithStyle: UITableViewStyleGrouped];
    dvc.detailItem = objectInCell;
    [self.navigationController pushViewController: dvc animated: YES];
}
ManagedObjectDetailTableViewController为每个属性和每个关系都有一行。在
didselectRowatinedexpath
中,如果选择了包含and属性的行,我将推送到ManagedObjectAttributeEditViewController视图控制器:

- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
    // Section 0 has the attributes for the 'detailItem' object alphabetically by name
    if( indexPath.section == 0 ) {
        ManagedObjectAttributeEditViewController *evc = [[ManagedObjectAttributeEditViewController alloc]
                    initWithNibName: @"ManagedObjectAttributeEditViewController" bundle: nil];
        evc.editedObject = self.detailItem;
        evc.delegate = self;
        // Figure out from the row which attribute was selected
        NSEntityDescription *entity = self.detailItem.entity;
        NSDictionary *attributes = entity.attributesByName;
        NSArray *keys = [attributes allKeys];
        keys = [keys sortedArrayUsingSelector: @selector(compare:)];
        NSString *key = [keys objectAtIndex: indexPath.row];
        evc.editedFieldKey = key;
        [self.navigationController pushViewController: evc animated: YES];
    // The other sections are the relationships for the 'detailItem' object
    } else {
        // Code omitted as not relevant for the error.
    }
}
ManagedObjectAttributeEditViewController具有文本字段等,以允许编辑属性值。触摸其“保存”按钮时,将执行:

- (IBAction) save {
    id valueFromView;
    NSAttributeType type = [self typeForEditedAttribute];
    switch( type ) {
    case NSDateAttributeType:
        valueFromView = self.datePicker.date;
        break;
    case NSStringAttributeType:
        if( [self.fieldKeyTester shouldUseTextViewForKey: self.editedFieldKey inEntity: self.editedObject.entity.name] ) {
            valueFromView = self.textView.text;
        } else {
            valueFromView = self.textField.text;
        }
        break;
    case NSInteger16AttributeType:
    case NSInteger32AttributeType:
    case NSInteger64AttributeType:
        valueFromView = [NSNumber numberWithInteger: [self.textField.text integerValue]];
        break;
    case NSDecimalAttributeType:
    case NSDoubleAttributeType:
    case NSFloatAttributeType:
        valueFromView = [NSNumber numberWithDouble: [self.textField.text doubleValue]];
        break;
    case NSBooleanAttributeType:
        valueFromView = [NSNumber numberWithBool: self.switchControl.isOn];
        break;
    case NSObjectIDAttributeType:
    case NSTransformableAttributeType:
    case NSBinaryDataAttributeType:
    case NSUndefinedAttributeType:
        NSLog( @"Don't know how to handle attribute type: %d in %s", type, __func__ );
        break;
    default:
        NSLog( @"Unrecognized attribute type: %d in %s", type, __func__ );
        break;
    }
    [self.delegate managedObjectAttributeEditViewController: self
            didSaveValue: valueFromView forKey: self.editedFieldKey];
}
ManagedObjectDetailTableViewController设置为委托,而
didSaveValue:forKey:
方法为:

- (void) managedObjectAttributeEditViewController: (ManagedObjectAttributeEditViewController *) controller didSaveValue: (id) value forKey: (NSString *) key {
    if( value && key ) {
        [self.detailItem setValue: value forKey: key];
        NSError *error;
        if( ![self.detailItem.managedObjectContext save: &error] ) {
            // Update to handle the error appropriately.
            NSLog( @"Unresolved error doing save of attribute %@.\n%@", key, error.localizedDescription );
        } else {
            NSLog( @"-- successfully saved" );
        }
    } else {
        NSLog( @"Got a cancel from edit attribute" );
    }
    // OK, the attribute editing view controller has told us it is done, pop it
    [self.navigationController popViewControllerAnimated: YES];
}
- (void) managedObjectAttributeEditViewController: (ManagedObjectAttributeEditViewController *) controller
             didSaveValue: (id) value forKey: (NSString *) key {
    if( value && key ) {
        [self.detailItem setValue: value forKey: key];
        NSError *error;
        if( ![self.detailItem.managedObjectContext save: &error] ) {
            // Update to handle the error appropriately.
            NSLog( @"Unresolved error doing save of attribute %@.\n%@", key, error.localizedDescription );
        } else {
            NSLog( @"-- successfully saved" );
            if( [self.detailItem.managedObjectContext.parentContext.hasChanges] ) {
                if( ![self.detailItem.managedObjectContext.parentContext save: &error] ) {
                    NSLog( @"Unresolved error doing save of parent context for attribute %@.\n%@", key, error.localizedDescription );
                } else {
                    NSLog( @"-- successfully saved the parent context too!" );
                }
            }
        }
    } else {
        NSLog( @"Got a cancel from edit attribute" );
    }
    // OK, the attribute editing view controller has told us it is done, pop it
    [self.navigationController popViewControllerAnimated: YES];
}
因此,如果我从实体的对象列表开始,并且它们被正确排序。我触摸一行并将其推送到ManagedObjectDetailTableViewController。我触摸其中的属性行,它将推送到ManagedObjectAttributeEditViewController。我更改值并触摸保存。这会弹出到ManagedObjectDetailTableViewController,在那里一切看起来都很好。然后,我点击后退按钮返回实体的对象列表,但现在它们不再排序(它们似乎总是以相同的顺序排列,但我无法识别顺序的模式)

如果在执行保存操作后,在触摸“后退”按钮之前计数到10,则列表将正确排序

如果在
didSaveValue:forKey:
中注释掉
[self.detailItem.managedObjectContext save:&error]
方法调用,则实体的对象列表仍保持正确排序,但如果在自动保存之前退出应用程序,则会丢失更改

这使我认为这与
[self.detailItem.managedObjectContext save:&error]
未完成以及获取的结果控制器(使用相同的NSManagedObjectContext)由于某种原因无法检索已排序的数据有关

我正在更改其值的属性不包含在排序描述符中,因此在修改值之前和之后显示的顺序应该相同。我的数据库相当大,可能需要几秒钟才能将其写入磁盘。我在模拟器和设备上看到了iOS 5.1的问题

有没有人经历过类似的事情或有什么建议

对不起,这是这么长的风和快乐的7月4日为所有这些Stackoverflowers在美国


修订的ManagedObjectDetailTableViewController委托方法
didSaveValue:forKey:
方法是:

- (void) managedObjectAttributeEditViewController: (ManagedObjectAttributeEditViewController *) controller didSaveValue: (id) value forKey: (NSString *) key {
    if( value && key ) {
        [self.detailItem setValue: value forKey: key];
        NSError *error;
        if( ![self.detailItem.managedObjectContext save: &error] ) {
            // Update to handle the error appropriately.
            NSLog( @"Unresolved error doing save of attribute %@.\n%@", key, error.localizedDescription );
        } else {
            NSLog( @"-- successfully saved" );
        }
    } else {
        NSLog( @"Got a cancel from edit attribute" );
    }
    // OK, the attribute editing view controller has told us it is done, pop it
    [self.navigationController popViewControllerAnimated: YES];
}
- (void) managedObjectAttributeEditViewController: (ManagedObjectAttributeEditViewController *) controller
             didSaveValue: (id) value forKey: (NSString *) key {
    if( value && key ) {
        [self.detailItem setValue: value forKey: key];
        NSError *error;
        if( ![self.detailItem.managedObjectContext save: &error] ) {
            // Update to handle the error appropriately.
            NSLog( @"Unresolved error doing save of attribute %@.\n%@", key, error.localizedDescription );
        } else {
            NSLog( @"-- successfully saved" );
            if( [self.detailItem.managedObjectContext.parentContext.hasChanges] ) {
                if( ![self.detailItem.managedObjectContext.parentContext save: &error] ) {
                    NSLog( @"Unresolved error doing save of parent context for attribute %@.\n%@", key, error.localizedDescription );
                } else {
                    NSLog( @"-- successfully saved the parent context too!" );
                }
            }
        }
    } else {
        NSLog( @"Got a cancel from edit attribute" );
    }
    // OK, the attribute editing view controller has told us it is done, pop it
    [self.navigationController popViewControllerAnimated: YES];
}

我现在了解到,这种双重保存对于使用最新iOS进行永久存储的更改是必要的,因为对于父上下文,保存只会上升一个级别。我不明白为什么不将save传播到永久存储会扰乱排序顺序。也许在我的代码中有其他一些bug,这是屏蔽的,或者可能只是它的工作方式…

好的,根据您的编辑,包括新的堆栈跟踪:

您正在使用UIManagedDocument来管理核心数据堆栈。这意味着第一次保存可能来自UIDocument的主线程上下文,第二次保存可能来自其后台上下文。能否确认您正在使用UIManagedDocument的managedObjectContext上下文中的对象(与您自己创建的其他上下文相反)

还有一件事。您能否确认
setupFetchedResultsController
方法中的
entitySortDescriptorList
不会以某种方式变为零

最后,与其他诊断一样:

  • 尝试从顶级视图控制器调用
    save:
    ,看看会发生什么
  • 如果为NSFetchedResultsController进行委托,会发生什么情况?你收到变更通知了吗?它们看起来合理吗

保存在主线程上,但我只更改了一个托管对象的一个属性,因此它不应该保存整个数据库,只保存一条记录,而且应该保存得非常快<代码>重新加载数据在我重置
获取的ResultsController
时触发。我将尝试设置观察者,看看我能学到什么。感谢您的建议。由于加载和卸载不同的视图控制器,我在尝试侦听通知时感到困惑,因此我在托管对象子类中添加了一个
willSave
。有趣的是,它在我调用
[self.detailItem.managedObjectContext save:&error]
后立即被调用,正如预期的那样,但大约10秒后,我没有做任何事情,它又被调用了。如果我在第二个程序之前终止该程序,那么下次运行应用程序时,数据库中不会出现更改!尝试查看或发布第二个通知的堆栈跟踪,以便您可以确定它来自何处。好的,查看堆栈跟踪有太多我无法识别的内容。我将把它们附在上述问题之后。我还研究了
NSManagedObject
hasChanges
属性、包含它的
NSManagedObjectContext
,以及
parentContext
它们从+、+、-到-、-、+(++==true/hasChanges和-==false/!hasChanges)关于我的保存,因此我怀疑我看到的延迟保存是来自
parentContext
NSFetchedResultsController
已经有一个委托