NSFetchedResultsController错误的节号

时间:2013-01-10 08:08:49

标签: ios core-data uitableview sections

我的步骤是:

  1. 推TVC。
  2. 使用托管对象上下文初始化新FRC。
  3. 执行提取。
  4. 有两个部分,每个部分都有一行。
  5. 执行FRC中使用的提取请求。
  6. 有两个元素的数组。
  7. 删除最后一行并保存MOC。
  8. Pop TVC。
  9. 推TVC。
  10. 使用相同的MOC初始化新的FRC。
  11. 执行提取。
  12. 有两个部分。
  13. 执行FRC中使用的提取请求。
  14. 获得包含ONE元素的数组。
  15. 我尝试在执行提取之前调用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];
    }
    
    ...
    

    TLCoreData:

    //
    //  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);
    

1 个答案:

答案 0 :(得分:0)

您应该为我们提供其他详细信息。

与此同时,我真的建议你看一下NSFetchedResultsControllerDelegate课程。您可以在类引用中找到需要实现的方法。

如果您为NSFetchedResultsController实例设置委托,例如

_fetchedController.delegate = self;

然后,代理将响应对Core Data模型所做的更改(添加或删除部分;添加,删除,移动或更新行)。

例如,如果您实施

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

如果您删除或添加了一个部分,系统会为您捕获更改。

希望它有所帮助。

修改

我的猜测是,如果TLMyTableViewController延伸CoreDataTableViewController,则表示您没有在

中调用正确的方法
- (void)prepareFetchedResultController {
    [self.model setupFetchedResultsControllerCardsListWithCompletion:^(NSFetchedResultsController *result){
        self.fetchedResultsController = result;
    }];
}

尝试使用

[self setFetchedResultsController:result];