且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

NSFetchedResultsController返回带有null indexPaths的对象

更新时间:2023-11-13 21:33:04

可以通过使用 - [NSFetchedResultsController fetchedObjects] objectAtIndex:]


Details are in the comments.

The following code:

// Perform the fetch...
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

// Confirm that objects were fetched by counting them...
NSLog(@"Number of Objects = %i",
      [[fetchedResultsController fetchedObjects] count]);

// Confirm that sections exist by counting them...
NSLog(@"Numbers of Sections = %i",
      [[fetchedResultsController sections] count]); 

for (id section in [fetchedResultsController sections]) {
    // Count number of objects in each section
    // _The fact that this outputs 0 is the first sign of trouble_
    NSLog(@"Number of Objects in Section = %i", [section numberOfObjects]);
}

for (Reminder *reminder in [fetchedResultsController fetchedObjects]) {
    // Confirm that the objects fetched are in fact real objects
    // by accessing their "textContent" property...
    NSLog(@"textContent=%@", reminder.textContent);

    // Show that the fetched objects are being returned 
    // with a (null) indexPath...
    // _The second sign of trouble..._
    NSLog(@"IndexPath=%@",
          [fetchedResultsController indexPathForObject:reminder]);
}

NSUInteger indexArr[] = {0,0};
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexArr 
                                                    length:2];

// _Application crashes on this line because the fetched 
// objects do not have indexPaths_
Reminder *testReminder = (Reminder *)[fetchedResultsController 
                                      objectAtIndexPath:indexPath]; 
NSLog(@"textContent = %@", testReminder.textContent);

Results in the following output:

2010-07-17 00:48:41.865 Reminders[27335:207] Number of Objects = 3
2010-07-17 00:48:41.867 Reminders[27335:207] Numbers of Sections = 1
2010-07-17 00:48:41.868 Reminders[27335:207] Number of Objects in Section = 0
2010-07-17 00:48:41.870 Reminders[27335:207] textContent=Imported Object 3
2010-07-17 00:48:41.871 Reminders[27335:207] IndexPath=(null)
2010-07-17 00:48:41.873 Reminders[27335:207] textContent=Imported Object 2
2010-07-17 00:48:41.873 Reminders[27335:207] IndexPath=(null)
2010-07-17 00:48:41.874 Reminders[27335:207] textContent=Imported Object 1
2010-07-17 00:48:41.875 Reminders[27335:207] IndexPath=(null)
2010-07-17 00:48:41.887 Reminders[27335:207] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'

Any ideas would be greatly appreciated. FYI, the above code works perfectly in a separate application if I use a different template as a starting point. I.e. if I use the "Window-based application" template, the code will fail. If I use "Navigation-based application" the code works as expected.

Update: TechZen was wondering if the problem is caused by my Reminder entity. I thought this was a good idea to look into, so I did the following:

  1. Create two default template applications: a "Window-based application" and a "Navigation-based Application" (both with Core Data enabled)

  2. Copied over the minimum code needed from the Nav-based to the Window-based to perform the above test (pretty much just the "xcdatamodel" file, the fetchedresultscontroller, and a way to add test objects).

The above code still fails in the new "Reminder-entity free" window-based application. (In this new test application there is in fact zero code I've authored myself (outside of the test code), it's all just template code cut-and-pasted together.)

So now, I am looking for any way to get the above code to run after creating a "Window-based application". Here is the code to perform the test using the nav-based's default entity, in case anyone is interested in giving it a try:

UPDATE Note that as TechZen noted below this code will crash no matter what if run with an empty database, so if starting from a window-based application, first add a few objects to the database then add the test code.

// Confirm that objects were fetched
NSLog(@"Number of Objects = %i",
      [[fetchedResultsController fetchedObjects] count]);

// Confirm that sections exist
NSLog(@"Numbers of Sections = %i",
      [[fetchedResultsController sections] count]); 

for (id section in [fetchedResultsController sections]) {

    // Count number of objects in sections
    // _The fact that this outputs 0 is the first sign of trouble_
    NSLog(@"Number of Objects in Section = %i", [section numberOfObjects]);
}

for (NSManagedObject *managedObject in [fetchedResultsController fetchedObjects]) {

    // Confirm that the objects fetched are in fact real objects, 
    // by accessing their "timeStamp" property
    NSLog(@"TimeStamp=%@", [[managedObject valueForKey:@"timeStamp"] description]);

    // Show that the fetched objects are being returned 
    // with a (null) indexPath
    // _The second sign of trouble..._
    NSLog(@"IndexPath=%@",
          [fetchedResultsController indexPathForObject:managedObject]);
}

NSUInteger indexArr[] = {0,0};
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexArr 
                                                    length:2];

// _Application crashes on this line, because the fetched 
// objects do not have indexPaths_
NSManagedObject *managedObject = [fetchedResultsController 
                                  objectAtIndexPath:indexPath];
NSLog(@"textContent = %@", [[managedObject valueForKey:@"timeStamp"] description]);

UPDATE here is the output when using the new cut-and-pasted code

2010-07-18 15:33:41.264 Reminders[30898:207] Number of Objects = 3
2010-07-18 15:33:41.266 Reminders[30898:207] Numbers of Sections = 1
2010-07-18 15:33:41.267 Reminders[30898:207] Number of Objects in Section = 0
2010-07-18 15:33:41.270 Reminders[30898:207] TimeStamp=2010-07-18 13:59:00 -0400
2010-07-18 15:33:41.271 Reminders[30898:207] IndexPath=(null)
2010-07-18 15:33:41.272 Reminders[30898:207] TimeStamp=2010-07-18 13:59:00 -0400
2010-07-18 15:33:41.273 Reminders[30898:207] IndexPath=(null)
2010-07-18 15:33:41.274 Reminders[30898:207] TimeStamp=2010-07-18 13:58:59 -0400
2010-07-18 15:33:41.275 Reminders[30898:207] IndexPath=(null)
2010-07-18 15:33:41.276 Reminders[30898:207] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'

UPDATE So I narrowed this problem down to being an SDK version-related issue, I now have a project that if I build with Simulator 3.2 it crashes, and build with Simulator 3.1.3 it works fine. BUT if I add a UITableViewController, then build with Simulator 3.2, then it works fine again. So I've created a new *** post to ask the question: If you are using NSFetchedResultsController without a UITableViewController, how do you interact with the objects? (since the IndexPaths are unreliable).

UPDATE This issue is (tentatively) solved by using -[NSFetchedResultsController fetchedObjects] objectAtIndex:] to access the objects.

This can be bypassed by using -[NSFetchedResultsController fetchedObjects] objectAtIndex:]