3

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

viewWillAppear:我的表格视图控制器中,我将获取的结果控制器设置为:

- (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 */
}

didSelectRowAtIndexPath这个 tableview 控制器中,我推送到一个详细的 table view 控制器,如下所示:

- (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每个属性和每个关系都有一行。如果选择了包含和属性的行,didSelectRowAtIndexPath我会推送到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];
}

因此,如果我从实体的对象列表开始,并且它们已正确排序。我触摸一行并将其推送到ManagedObjectDetailTableViewController。我触摸其中的一个属性行,它会推送到ManagedObjectAttributeEditViewController。我更改值并触摸save。这会弹出到ManagedObjectDetailTableViewController,一切看起来都很好。然后我触摸后退按钮以返回实体的对象列表,但现在它们不再排序(它们似乎总是按相同的顺序,但我不认识该顺序的模式)。

如果我在保存后并且在触摸后退按钮之前数到 10,则列表已正确排序。

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

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

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

有没有人经历过这样的事情或有什么建议?

抱歉,对于美国的所有 Stackoverflowers 来说,7 月 4 日如此冗长而快乐!


修改后的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" );
            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 将其更改为永久存储是必要的,因为在父上下文中,保存只会上升一个级别。我不明白为什么不将保存传播到永久商店应该打乱排序顺序。也许我的代码中的某个地方还有其他一些错误,这是掩盖的,或者它只是它的工作方式......

4

1 回答 1

1

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

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

另一件事。你能确认entitySortDescriptorList你的方法没有以某种方式变成零setupFetchedResultsController吗?

最后,作为其他诊断:

  • 尝试save:从您的顶级视图控制器调用,看看会发生什么。
  • 如果你为你的 NSFetchedResultsController 做一个委托会发生什么?您收到更改通知吗?它们看起来合理吗?
于 2012-07-04T20:37:16.427 回答