获取获取请求后无法更新Core Data Model

时间:2014-01-13 18:11:55

标签: ios core-data uitableview nsfetchrequest

我的应用程序设置如下,我有两个视图控制器来自我的RootViewController - 一个带有两个按钮的简单主选择页面。顶部按钮将我发送到ViewController 1,在那里我拍摄照片并在有关照片的6个文本字段中插入数据。然后我点击一个保存按钮,将这些实体保存到我的ManagedObjectContext中的ManagedObjectModel TargetData。主页上的第二个按钮指向TableVIewController,我在其中调用NSFetchedResults sorta来更新TableView。

它工作得很好......至少在我运行一次TableViewController之前。我可以添加尽可能多的带有文本数据的照片,直到我显示TableViewController,此时,在离开页面时,TableViewController将仅显示第一次打开页面时加载的对象,而不是之后添加的任何项目。我做了一些搜索,发现第一次加载TableVIew后,fetchResults只看到它第一次加载时看到的很多实体。例如,如果我启动了应用程序,添加了5张带有文本的照片,然后运行了TableViewController,我会看到正确显示的5个项目。我可以回到主页然后添加另一张带文字的照片,但如果我回到TableVIewController,我只会看到我在打开TableViewController之前添加的5张照片。

在TableViewController的类中,我已经设置了我在TableViewController.m文件中使用它的所有普通方法。

完全无能为力。救命啊!

编辑:以下是我的一些代码

来自:将保存数据的ViewController.m

    - (IBAction)saveTarget:(id)sender {
    //Only save if there is an image other than the stock photo
    if (self.image != [UIImage imageNamed:@"Photo-Video-Slr-camera-icon"]) {

        //Grab the main ManagedObjectContext from the AppDelegate
        sTCAppDelegate *appDelegate =[[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context =[appDelegate managedObjectContext];
        //Get the Target model from CoreData
        [self setTarget:[NSEntityDescription insertNewObjectForEntityForName:@"TargetData" inManagedObjectContext:context]];

        //Set the properties of the TargetData model
        self.target.weaponData = self.weaponData.text;
        self.target.bulletType = self.bulletType.text;
        self.target.stanceType = self.stanceType.text;
        self.target.distanceData = self.distanceData.text;
        self.target.targetNotes = self.targetNotes.text;
        self.target.sightType = self.sightType.text;
        self.target.scoreData = [NSNumber numberWithInt:[self.scoreData.text intValue]];

        //Set image to smaller size for storage
        UIImage *image = [self resizeImage:self.image toWidth:50 andHeight:50];
        //Save as PNG NSData for compression
        self.target.targetImage = UIImagePNGRepresentation(image);

        //Set a date property to use for organizing by most recently saved
        self.target.timeStamp = [NSDate date];

        //Save to context
        NSError *error = nil;
        if ( ![context save:&error] ){
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }

        //Jump back to main page
        [self.navigationController popToRootViewControllerAnimated:YES];

        //set images back to nil for next time AddTarget is opened
        image = nil;
        self.image = nil;
        NSLog(@"Data Saved");
    }
    //Return alert if user has not entered a photo
    else{
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"We're Sorry!" message:@"You must enter at least a photo to save target data." delegate:nil cancelButtonTitle:@"Okay" otherButtonTitles:nil];
        [alert show];
    }
}

来自:显示数据的TableViewController.m

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    NSLog(@"number of sections:%lu",(unsigned long)[[self.fetchedResultsController sections] count]);
    return [[self.fetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    NSLog(@"%lu", [sectionInfo numberOfObjects]);
    return [sectionInfo numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    NSLog(@"Ran Cell Configure");
    [self configureCell:cell atIndexPath:indexPath];
    // Configure the cell...

    return cell;
}
-(void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath{

    TargetData *target = [self.fetchedResultsController objectAtIndexPath:indexPath];
    UIImage *image = [UIImage imageWithData:target.targetImage];
    cell.imageView.image = image;
    cell.textLabel.text = [NSString stringWithFormat:@"%@", target.scoreData];
    cell.detailTextLabel.text = target.weaponData;
}

#pragma mark- FetchedResultsControllerDelegate Methods

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }
    sTCAppDelegate *appDelegate =[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context =[appDelegate managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"TargetData" inManagedObjectContext:context];

    [fetchRequest setEntity:entity];
    [fetchRequest setFetchBatchSize:0];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedRequestsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:@"Master"];
    aFetchedRequestsController.delegate = self;
    self.fetchedResultsController = aFetchedRequestsController;

    NSError *error = nil;

    if (![self.fetchedResultsController performFetch:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _fetchedResultsController;
}

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)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)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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}

- (IBAction)mainMenu:(id)sender {
    [self.navigationController popToRootViewControllerAnimated:YES];
}

所以是的,我正在使用TableViewController实现中的方法。

编辑2

我通常在我的iPhone上运行我的应用程序来测试它,因为我可以在我的设备中使用相机。要查看控制台可能对我的问题说些什么,我将一些图像添加到模拟器并在模拟器上运行。这次,在添加带有文本的照片后,打开TableViewController,然后添加另一张照片,在尝试再次打开TableViewController后,我收到了一个巨大的崩溃错误报告。

以下是错误的终止部分:

2014-01-13 13:09:38.759 Target Tracker [25124:70b] ***由于未捕获的异常'NSInternalInconsistencyException'而终止app,原因:'CoreData:FATAL ERROR:段信息的持久缓存不匹配目前的配置。您已经非法改变了NSFetchedResultsController的获取请求,其谓词或其排序描述符,而没有禁用缓存或使用+ deleteCacheWithName:'

任何线索是什么意思?

分辨

问题是当我运行TableViewController时,我正在缓存FetchResults。当我向模型添加另一个实体并尝试返回一个新的fetchResult时,它与返回了一个关键CoreData错误的缓存版本不匹配。我没有看到它是一个错误,因为我最初没有在模拟器中运行应用程序,而是在我的实际设备上。一旦我在模拟器中运行它,我就能看到这个错误。

简而言之 - 我初始化NSFetchedResultsController时需要将“cacheName”设置为nil

NSFetchedResultsController *aFetchedRequestsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];

有关详细信息,请参阅此帖子:NSFetchedResultsController crashing on performFetch: when using a cache

4 个答案:

答案 0 :(得分:1)

问题是当我运行TableViewController时,我正在缓存FetchResults。当我向模型添加另一个实体并尝试返回一个新的fetchResult时,它与返回了一个关键CoreData错误的缓存版本不匹配。我没有看到它是一个错误,因为我最初没有在模拟器中运行应用程序,而是在我的实际设备上。一旦我在模拟器中运行它,我就能看到这个错误。

简而言之 - 我初始化NSFetchedResultsController时需要将“cacheName”设置为nil

NSFetchedResultsController *aFetchedRequestsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];

有关详细信息,请参阅此帖子:NSFetchedResultsController crashing on performFetch: when using a cache

答案 1 :(得分:0)

您确定在向其添加对象后保存上下文吗? 插入对象后,请调用

- (BOOL)save:(NSError **)error;

答案 2 :(得分:0)

NSFetchedResultsController在收到保存上下文通知之前不会更新。这通常在您调用方法时执行:

[yourManagedObjectContext save:&error]

当获取的结果控制器收到更新时,会对其委托执行一些调用,请参阅名为NSFetchedResultsControllerDelegate的文档,并实现所有更新tableview的方法。这是正确的方法,您也可以只实现一种方法来查看是否有效:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    // In the simplest, most efficient, case, reload the table view.
    [self.tableView reloadData];
}

答案 3 :(得分:0)

听起来你没有实现NSFetchedResultsController委托方法。也许会显示一些代码UITableViewController

更新

好的,你的代码看起来不错;非常好。

所以明显的,简单的可能性已经出来了。下一步是将一些断点(或日志)放入-saveTarget:-fetchedResultsController和委托方法中,并确保一切都在触发。

使用您提供的代码,您应该看到更新。这暗示了没有被解雇的事情。