0

我的步骤是:

  1. 推TVC。
  2. 使用托管对象上下文初始化新 FRC。
  3. 执行抓取。
  4. 有两个部分,每个部分有一行。
  5. 执行 FRC 中使用的获取请求。
  6. 得到包含两个元素的数组。
  7. 删除最后一行并保存 MOC。
  8. 流行电视节目。
  9. 推TVC。
  10. 使用相同的 MOC 初始化新的 FRC。
  11. 执行抓取。
  12. 有两个部分。
  13. 执行 FRC 中使用的获取请求。
  14. 得到一个元素的数组。

我尝试在执行提取之前调用 deleteCacheWithName 但没有成功。

我不明白该怎么做。

崩溃日志:

2013-01-10 12:27:47.948 MyApp[59830:c07] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[_PFArray objectAtIndex:]: index (1) beyond bounds (1)'

应用程序崩溃的代码行:

Card *card = [self.fetchedResultsController objectAtIndexPath:indexPath];

这是斯坦福课程的 CoreDataTableViewController.m:

//
//  CoreDataTableViewController.m
//
//  Created for Stanford CS193p Fall 2011.
//  Copyright 2011 Stanford University. All rights reserved.
//

#import "CoreDataTableViewController.h"

@interface CoreDataTableViewController()

@property (nonatomic) BOOL beganUpdates;

@end

@implementation CoreDataTableViewController

#pragma mark - Fetching

- (void)performFetch {
    if (self.fetchedResultsController) {
        NSLog(@"TVC: Perform fetch");

        NSError *error;
        [self.fetchedResultsController performFetch:&error];

        if (error)
            NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);

        NSLog(@"TVC: Number of fetched objects in FRC: %d", self.fetchedResultsController.fetchedObjects.count);
        NSLog(@"TVC: Number of sections in FRC: %d", self.fetchedResultsController.sections.count);
    } else {
        NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }

    [self.tableView reloadData];
}
- (void)setFetchedResultsController:(NSFetchedResultsController *)newfrc {
    NSFetchedResultsController *oldfrc = _fetchedResultsController;
    if (newfrc != oldfrc) {
        _fetchedResultsController = newfrc;
        newfrc.delegate = self;
        if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title)) {
            self.title = newfrc.fetchRequest.entity.name;
        }
        if (newfrc) {
            [self performFetch];
        } else {
            [self.tableView reloadData];
        }
    }
}

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSUInteger numberOfSections = self.fetchedResultsController.sections.count;
    NSLog(@"TVC: DataSource, number of sections: %d", numberOfSections);

    return numberOfSections;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSArray *sections = self.fetchedResultsController.sections;
    if (sections.count == 0) {
        NSLog(@"TVC: DataSource, there are no sections");
        return 0;
    }

    id<NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:section];
    NSUInteger numberOfRows = sectionInfo.numberOfObjects;
    NSLog(@"TVC: DataSource, number of rows: %d, in section: %d", numberOfRows, section);

    return numberOfRows;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return [self.fetchedResultsController sectionIndexTitles];
}

#pragma mark - NSFetchedResultsControllerDelegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) {
        [self.tableView beginUpdates];
        self.beganUpdates = YES;
    }
}
- (void)controller:(NSFetchedResultsController *)controller
  didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex
     forChangeType:(NSFetchedResultsChangeType)type {
    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
    {
        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 {
    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
    {
        switch(type)
        {
            case NSFetchedResultsChangeInsert:
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

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

            case NSFetchedResultsChangeUpdate:
                [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeMove:
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;
        }
    }
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    if (self.beganUpdates)
        [self.tableView endUpdates];
}
- (void)endSuspensionOfUpdatesDueToContextChanges {
    _suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
}
- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend {
    if (suspend) {
        _suspendAutomaticTrackingOfChangesInManagedObjectContext = YES;
    } else {
        [self performSelector:@selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0];
    }
}

- (void)logFetchedSections:(NSArray *)sections {
}

@end

这是我的模型的一部分:

//
//  TLModel.m
//

...

@property (nonatomic, strong) TLCoreData *coreData;

...


- (void)setupFetchedResultsControllerCardsListWithCompletion:(void (^)(NSFetchedResultsController *result))completion {
    [self.coreData performWithDocument:^(UIManagedDocument *document) {
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Card"];
        request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"named"
                                                                                         ascending:YES
                                                                                          selector:@selector(localizedCaseInsensitiveCompare:)]];
        NSError *error;
        NSArray *matches = [document.managedObjectContext executeFetchRequest:request error:&error];
        NSLog(@"Model: Execute fetch request, array count: %d", matches.count);

        NSFetchedResultsController *result = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                                 managedObjectContext:document.managedObjectContext
                                                                                   sectionNameKeyPath:@"named"
                                                                                            cacheName:nil];
        NSLog(@"Model: Created new FRC: %@", result);

        completion(result);
    }];
}

...

这是TVC:

//
//  TLMyTableViewController.m
//

...


- (void)prepareFetchedResultController {
    [self.model setupFetchedResultsControllerCardsListWithCompletion:^(NSFetchedResultsController *result){
        self.fetchedResultsController = result;
    }];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"My Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    // Configure the cell...
    Card *card = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = card.number;
    cell.detailTextLabel.text = card.option;

    return cell;
}
- (void)viewDidLoad {
    NSLog(@"TVC: Did load: %@", self);
    [self prepareFetchedResultController];
}

...

TLCore数据:

//
//  TLCoreData.m
//

#import "TLCoreData.h"
#import <CoreData/CoreData.h>
#import "TLManagedDocument.h"

@interface TLCoreData()

@property (nonatomic, strong) TLManagedDocument *document;

@end

@implementation TLCoreData

/*
static TLCoreData *_sharedInstance;

+ (TLCoreData *)sharedDocumentHandler {
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        _sharedInstance = [[self alloc] init];
    });

    return _sharedInstance;
}
*/

- (TLManagedDocument *)document {
    if (!_document) {
        NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
        url = [url URLByAppendingPathComponent:@"DataUsage.db"];
        _document = [[TLManagedDocument alloc] initWithFileURL:url];
    }

    return _document;
}

- (void)performWithDocument:(OnDocumentReady)onDocumentReady {
    void (^OnDocumentDidLoad)(BOOL) = ^(BOOL success) {
        if(success) {
#ifdef DEBUG
            NSLog(@"Current context: %@", self.document.managedObjectContext);
#endif
            onDocumentReady(self.document);
        }
#ifdef DEBUG
        else
            NSLog(@"Core Data: Managed document does not ready");
#endif
    };

    if (![[NSFileManager defaultManager] fileExistsAtPath:[self.document.fileURL path]]) {
#ifdef DEBUG
        NSLog(@"Core Data: Initialized document: %@", self.document.fileURL);
#endif
        [self.document saveToURL:self.document.fileURL
                forSaveOperation:UIDocumentSaveForCreating
               completionHandler:OnDocumentDidLoad];
    } else if (self.document.documentState == UIDocumentStateClosed) {
#ifdef DEBUG
        NSLog(@"Core Data: Opened document: %@", self.document.fileURL);
#endif
        [self.document openWithCompletionHandler:OnDocumentDidLoad];
    } else if (self.document.documentState == UIDocumentStateNormal) {
        OnDocumentDidLoad(YES);
    }
}

- (void)objectsDidChange:(NSNotification *)notification {
#ifdef DEBUG
    NSLog(@"Core Data: Objects changed in context: %@", self.document.managedObjectContext);
#endif
}
- (void)contextDidSave:(NSNotification *)notification {
#ifdef DEBUG
    NSLog(@"Core Data: Context saved: %@", self.document.managedObjectContext);
#endif
}

- (id)init {
    self = [super init];
    if (self) {

        // Set our document up for automatic migrations
        NSDictionary *options = @{
            NSMigratePersistentStoresAutomaticallyOption : @YES,
            NSInferMappingModelAutomaticallyOption : @YES
        };
        self.document.persistentStoreOptions = options;

        // Register for notifications
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(objectsDidChange:)
                                                     name:NSManagedObjectContextObjectsDidChangeNotification
                                                   object:self.document.managedObjectContext];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(contextDidSave:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:self.document.managedObjectContext];
    }

    return self;
}
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:NSManagedObjectContextDidSaveNotification
                                                  object:self.document.managedObjectContext];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:NSManagedObjectContextObjectsDidChangeNotification
                                                  object:self.document.managedObjectContext];
}

@end

日志是:

2013-01-10 12:27:43.291 MyApp[59830:c07] App: Launched
2013-01-10 12:27:43.354 MyApp[59830:c07] Core Data: Opened document: file://localhost/Users/itsme/Library/Application%20Support/iPhone%20Simulator/6.0/Applications/998028DF-FACC-4EFF-A5C7-B286B91F1100/Documents/DataUsage.db/
2013-01-10 12:27:43.354 MyApp[59830:c07] Model: Logging started
2013-01-10 12:27:43.357 MyApp[59830:c07] App: Did become active
2013-01-10 12:27:43.371 MyApp[59830:c07] Current context: <NSManagedObjectContext: 0x8185590>
2013-01-10 12:27:43.374 MyApp[59830:c07] Current context: <NSManagedObjectContext: 0x8185590>
2013-01-10 12:27:44.746 MyApp[59830:c07] TVC: Did load: <TLMyTableViewController: 0x8566100>
2013-01-10 12:27:44.746 MyApp[59830:c07] Current context: <NSManagedObjectContext: 0x8185590>
2013-01-10 12:27:44.747 MyApp[59830:c07] Model: Execute fetch request, array count: 2
2013-01-10 12:27:44.748 MyApp[59830:c07] Model: Created new FRC: <NSFetchedResultsController: 0x747bbb0>
2013-01-10 12:27:44.748 MyApp[59830:c07] TVC: Perform fetch
2013-01-10 12:27:44.750 MyApp[59830:c07] TVC: Number of fetched objects in FRC: 2
2013-01-10 12:27:44.750 MyApp[59830:c07] TVC: Number of sections in FRC: 2
2013-01-10 12:27:44.751 MyApp[59830:c07] TVC: DataSource, number of sections: 2
2013-01-10 12:27:44.752 MyApp[59830:c07] TVC: DataSource, number of rows: 1, in section: 1
2013-01-10 12:27:44.752 MyApp[59830:c07] TVC: DataSource, number of rows: 1, in section: 0
2013-01-10 12:27:46.346 MyApp[59830:c07] Current context: <NSManagedObjectContext: 0x8185590>
2013-01-10 12:27:46.347 MyApp[59830:c07] Model: Deleting a row
2013-01-10 12:27:46.349 MyApp[59830:c07] TVC: DataSource, number of sections: 1
2013-01-10 12:27:46.349 MyApp[59830:c07] TVC: DataSource, number of sections: 1
2013-01-10 12:27:46.349 MyApp[59830:c07] TVC: DataSource, number of rows: 1, in section: 0
2013-01-10 12:27:46.350 MyApp[59830:c07] Core Data: Objects changed in context: <NSManagedObjectContext: 0x8185590>
2013-01-10 12:27:46.351 MyApp[59830:c07] Core Data: Context saved: <NSManagedObjectContext: 0x8185590>
2013-01-10 12:27:47.941 MyApp[59830:c07] TVC: Did load: <TLMyTableViewController: 0x747d5e0>
2013-01-10 12:27:47.941 MyApp[59830:c07] Current context: <NSManagedObjectContext: 0x8185590>
2013-01-10 12:27:47.942 MyApp[59830:c07] Model: Execute fetch request, array count: 1
2013-01-10 12:27:47.942 MyApp[59830:c07] Model: Created new FRC: <NSFetchedResultsController: 0x74b9410>
2013-01-10 12:27:47.942 MyApp[59830:c07] TVC: Perform fetch
2013-01-10 12:27:47.944 MyApp[59830:c07] TVC: Number of fetched objects in FRC: 1
2013-01-10 12:27:47.944 MyApp[59830:c07] TVC: Number of sections in FRC: 2
2013-01-10 12:27:47.944 MyApp[59830:c07] TVC: DataSource, number of sections: 2
2013-01-10 12:27:47.945 MyApp[59830:c07] TVC: DataSource, number of rows: 1, in section: 1
2013-01-10 12:27:47.945 MyApp[59830:c07] TVC: DataSource, number of rows: 1, in section: 0
2013-01-10 12:27:47.948 MyApp[59830:c07] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[_PFArray objectAtIndex:]: index (1) beyond bounds (1)'

这一行说明了数组中错误的节数:

2013-01-10 12:27:47.944 MyApp[59830:c07] TVC: Number of sections in FRC: 2

本日志源代码:

NSLog(@"TVC: Number of sections in FRC: %d", self.fetchedResultsController.sections.count);
4

1 回答 1

0

You should provide other details for us.

In the meantime I really suggest to take a look at NSFetchedResultsControllerDelegate class. You can find the method you need to implement in the class reference.

If you set the delegate for your NSFetchedResultsController instance like

_fetchedController.delegate = self;

then, the delegate will respond to changes made to the Core Data model (add or delete sections; add, delete, move or update rows).

For example, if you implement

- (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;
    }
}

and you remove or add a section, changes will be captured for you.

Hope it helps.

Edit

My guess is that if TLMyTableViewController extends CoreDataTableViewController, you are not calling the right method in

- (void)prepareFetchedResultController {
    [self.model setupFetchedResultsControllerCardsListWithCompletion:^(NSFetchedResultsController *result){
        self.fetchedResultsController = result;
    }];
}

try to use instead

[self setFetchedResultsController:result];
于 2013-01-10T09:29:42.533 回答