ios - 更新 CoreData 对象无法正常工作

标签 ios iphone objective-c uitableview core-data

我是 iOS 开发/CoreData 的新手,我已经搜索了我的特定问题的答案,但还没有找到。

我有一个 TableView,其中包含我数据库中的一些对象。每个对象都有一个属性“名称”。如果我打开其中一个对象的详细信息 View 并使用“后退”按钮返回到 tableView,一切正常。如果我不更改“名称”TextField 并按“完成”按钮返回到我的 tableView,它也可以工作。一旦我在“名称”TextField 中做了一些更改,我就会收到此错误:

Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (9) must be equal to the number of rows contained in that section before the update (9), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).

为什么有对象被删除了?

这是我的 DetailsS​​taticTableViewController.m 的代码:

#import "WeinStaticTableViewController.h"
#import "Wein.h"
#import "JSMToolBox.h"

@interface WeinStaticTableViewController ()

@end

@implementation WeinStaticTableViewController
@synthesize nameTextView;
@synthesize landTextView;
@synthesize herstellerTextView;
@synthesize wein = _wein;

- (id)initWithStyle:(UITableViewStyle)style
{
   self = [super initWithStyle:style];
   if (self) {
     // Custom initialization
   }
   return self;
}

- (void)viewDidLoad
{
   [super viewDidLoad];

   if (self.wein != nil) {
      self.nameTextView.text = self.wein.name;
   }
}

- (void) viewDidDisappear:(BOOL)animated
{
   [[JSMCoreDataHelper managedObjectContext] rollback];
   [super viewDidDisappear:animated];
}

- (void)didReceiveMemoryWarning
{
   [super didReceiveMemoryWarning];
   // Dispose of any resources that can be recreated.
   [self setNameTextView:nil];
   [self setLandTextView:nil];
   [self setHerstellerTextView:nil];
   self.wein = nil;
}

- (IBAction)barButtonItemDonePressed:(id)sender
{
   NSLog(@"%s", __PRETTY_FUNCTION__);
   if (self.wein != nil) {
       self.wein.name = self.nameTextView.text;
       [JSMCoreDataHelper saveManagedObjectContext:[JSMCoreDataHelper managedObjectContext]];
   }
   [self.navigationController popViewControllerAnimated:YES];
}
@end

这是我的 JSMCoreDataHelper.m 的代码:

#import "JSMCoreDataHelper.h"

@implementation JSMCoreDataHelper

+ (NSString*) directoryForDatabaseFilename
{
   return [NSHomeDirectory() stringByAppendingString:@"/Library/Private Documents"];
}

+ (NSString*) databaseFileName
{
   return @"database.sqlite";
}

+ (NSManagedObjectContext*) managedObjectContext
{
  static NSManagedObjectContext *managedObjectContext;
  if (managedObjectContext != nil) {
      return managedObjectContext;
  }

  NSError *error;
  // Verzeichnis unter dem das Datenbank-File abgespeichert werden soll, wird neu erzeugt, falls es noch nicht existiert.
  [[NSFileManager defaultManager] createDirectoryAtPath:[JSMCoreDataHelper directoryForDatabaseFilename] withIntermediateDirectories:YES attributes:nil error:&error];

  if (error) {
      NSLog(@"Fehler: %@", error.localizedDescription);
      return nil;
  }

  NSString *path = [NSString stringWithFormat:@"%@/%@", [JSMCoreDataHelper directoryForDatabaseFilename], [JSMCoreDataHelper databaseFileName]];
  NSURL *url = [NSURL fileURLWithPath:path];

  NSManagedObjectModel *managedModel = [NSManagedObjectModel mergedModelFromBundles:nil];

  NSPersistentStoreCoordinator *storeCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedModel];

  if (! [storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]) {
      NSLog(@"Fehler: %@", error.localizedDescription);
      return nil;
  }

  managedObjectContext = [[NSManagedObjectContext alloc] init];
  managedObjectContext.persistentStoreCoordinator = storeCoordinator;

  return managedObjectContext;
}

+ (id) insertManagedObjectOfClass: (Class) aClass inManagedObjectContext: (NSManagedObjectContext*) managedObjectContext
{
   NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(aClass) inManagedObjectContext:managedObjectContext];
   return managedObject;
}

+ (BOOL) saveManagedObjectContext: (NSManagedObjectContext*) managedObjectContext
{
   NSError *error;
   if (! [managedObjectContext save:&error]) {
       NSLog(@"Fehler: %@", error.localizedDescription);
       return NO;
   }
   return YES;
}

+ (NSArray*) fetchEntitiesForClass: (Class) aClass withPredicate: (NSPredicate*) predicate inManagedObjectContext: (NSManagedObjectContext*) managedObjectContext
{
   NSError *error;
   NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
   NSEntityDescription *entityDescription = [NSEntityDescription entityForName:NSStringFromClass(aClass) inManagedObjectContext:managedObjectContext];
   fetchRequest.entity = entityDescription;
   fetchRequest.predicate = predicate;

   NSArray *items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
   if (error) {
       NSLog(@"Fehler: %@", error.localizedDescription);
       return nil;
   }
   return items;
}

+ (BOOL) performFetchOnFetchedResultsController: (NSFetchedResultsController*) fetchedResultsController
{
   NSError *error;
   if (! [fetchedResultsController performFetch:&error]) {
       NSLog(@"Fehler: %@", error.localizedDescription);
       return NO;
   }
   return YES;
}

@end

最后是我的 MainTableViewController 的 numberOfSections 和 numberOfRowsInSection:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
   // Return the number of sections.
   return [[self.fetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
   // Return the number of rows in the section.
   return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}

任何线索都可以帮助...谢谢!

最佳答案

这是我使用 NSFetchedResultsController 的代码,这与您管理 CoreData 的代码配合得很好。

希望这有帮助。

#import "Person.h"
#import "mainViewController.h"
#import "JSMCoreDataHelper.h"
#import "staticInfoViewController.h"

@implementation mainViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Load From CoreData
    NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }
}

#pragma mark - TableView DataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return [[self.fetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    Person *p = [_fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:@"Name: %@", p.name];
}

#pragma mark - TableView Delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    staticInfoViewController *info = [[staticInfoViewController alloc] initWithNibName:@"staticInfoViewController" bundle:[NSBundle mainBundle]];
    info.currentPerson = [_fetchedResultsController objectAtIndexPath:indexPath];
    [self.navigationController pushViewController:info animated:YES];
}

#pragma mark - Set NSFetchedResultsController

- (NSFetchedResultsController *)fetchedResultsController {

    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription
                                   entityForName:@"Person" inManagedObjectContext:[JSMCoreDataHelper managedObjectContext]];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc]
                              initWithKey:@"name" ascending:YES];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

    [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                        managedObjectContext:[JSMCoreDataHelper managedObjectContext] sectionNameKeyPath:nil
                                                   cacheName:@"cache"];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;
}

#pragma mark - NSFetchedResultsController Delegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray
                                               arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray
                                               arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.tableView endUpdates];
}

@end

关于ios - 更新 CoreData 对象无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20220721/

相关文章:

iphone - 如何用Core Graphics制作渐变效果?

iphone - 保存时出现核心数据 Unresolved 错误

ios - 未调用 prepareForSegue

ios - 如何将 TableViewCell 值传递给 Swift 3.0 中的新 ViewController?

ios - 用于图像的3维查找字典?

iphone - NSCalendar dateFromComponents 在使用星期时返回错误值

ios - UILocalNotification - 播放超过 30 秒的声音

ios - Zip Archive Progress 分配了不兼容的 block 指针类型

ios - 如何在 JSQMessagesViewController 中显示消息日期

ios - 避免 Swift 同时调用 viewDidLoad 方法得到 "Index out of range"