NSFetchedResultsController index beyond bounds - iphone

I'm using an NSFetchedResultsController to display items in my table view:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [[self.fetchedResultsController fetchedObjects] count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TagCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];;
}
Tag *tag = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = tag.name;
return cell;
}
However, this code breaks at this line:
Tag *tag = [self.fetchedResultsController objectAtIndexPath:indexPath];
With this message:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'
I've NSLogged [self.fetchedResultsController fetchedObjects] and I can confirm that there are indeed Tag objects. If I replace that above line with this everything works as expected:
Tag *tag = [[self.fetchedResultsController fetchedObjects] objectAtIndex:indexPath.row];
I NSLogged indexPath and the indexes are {0, 0} (section 0 row 0) so I know it isn't an issue with the section. I'm extremely confused as to why this is happening because theoretically, those two pieces of code do the same thing. Any help is appreciated.
Thanks
UPDATES:
id section = [[[self fetchedResultsController] sections] objectAtIndex:[indexPath section]];
NSLog(#"Section %#", section); <-- returns a valid section
This code results in the same exception: Tag *tag = [[section objects] objectAtIndex:[indexPath row];
If I NSLog [section objects] it returns an empty array. I'm not sure why [fetchedResultsController fetchedObjects] returns an array with the right objects, and [section objects] returns nothing. Does this mean that the objects I'm creating have no section? Here's the code that I use to add new objects:
- (void)newTagWithName:(NSString *)name
{
NSIndexPath *currentSelection = [self.tableView indexPathForSelectedRow];
if (currentSelection != nil) {
[self.tableView deselectRowAtIndexPath:currentSelection animated:NO];
}
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
Tag *newTag = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:self.managedObjectContext];
// Configure new tag
newTag.name = name;
[self saveContext];
NSIndexPath *rowPath = [self.fetchedResultsController indexPathForObject:newTag];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:rowPath] withRowAnimation:UITableViewRowAnimationTop];
[self.tableView selectRowAtIndexPath:rowPath animated:YES scrollPosition:UITableViewScrollPositionTop];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
And here's my saveContext method:
- (void)saveContext
{
// Save changes
NSError *error;
BOOL success = [self.managedObjectContext save:&error];
if (!success)
{
UIAlertView *errorAlert = [[[UIAlertView alloc] initWithTitle:#"Error encountered while saving." message:nil delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil] autorelease];
[errorAlert show];
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
Am I doing something wrong here?

I ran into the same problem. In my case, it seems to be a corrupt cache file. Try renaming your cache, or calling deleteCacheWithName:.

When you initialized your fetch request controller, you gave it a sectionNameKeyPath: even though your data has no sections. Then you hard coded your tables section number to 1.
The fetch request controller is trying to return an object at section index zero in a data structure that has no sections at all.
I reproduced your error in the default navigation template app by changing the sectionNameKeyPath from nil to the name of the entity's sole attribute.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"timeStamp" cacheName:#"Root"];
... and then changed
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// return [[fetchedResultsController sections] count];
return 1;
}
and I get:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'
Set the sectionNameKeyPath:nil and that should fix your problem.

Are you compiling against, targeting or testing on 3.0?
What do you get back from the following code:
id section = [[[self fetchedResultsController] sections] objectAtIndex:[indexPath section]];
NSLog(#"Section %#", section);
Are you getting a valid section back?
If so, try adding the following line:
Tag *tag = [[section objects] objectAtIndex:[indexPath row];
If that produces an error then I suspect the issue is in your -tableView: numberOfRowsInSection: and that it may be giving back the wrong number of rows to the UITableView. Can you edit your question and post the code for that method as well?
Update
On further review I see where TechZen was pointing. You are telling the table view that you have one section when you may not. Further you are telling it how many objects your entire NSFetchedResultsController has when you should be answering it a little more succinctly.
Here is a slight change to your code.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
Make those changes and see what impact that has.
Question
What other UITableViewDataSource methods have you implemented? Sounds like there may be at least one more lingering error in your implementation.

got this error last night and it drove me crazy. I found that if you use the storyboard to specify the number of rows and sections, it needs to match the number you specify in your data source methods. To completely fix the error, you can set everything to 0 in the attributes inspector and just do it all programmatically, or just make sure that both data source methods and attributes inspector reflect the same amount of rows and sections.
^^

Related

Sorted arrays doesn't delete what I want

I have a sorted NSMutableArray which works perfectly and all though when I try to delete an object it crashes the app then when I reload the app it didn't delete the right one.
I known that is due to the fact that this is now a sorted array because before I implemented this feature it worked fine though I haven't got a clue of how to fix it.
Here is the code I use to delete things from the array:
- (void) tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if ( editingStyle == UITableViewCellEditingStyleDelete ) {
Patient *thisPatient = [patients objectAtIndex:indexPath.row];
[patients removeObjectAtIndex:indexPath.row];
if (patients.count == 0) {
[super setEditing:NO animated:YES];
[self.tableView setEditing:NO animated:YES];
}
[self deletePatientFromDatabase:thisPatient.patientid];
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
}
It is being stopped at this line:
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
Here is the code that I use for sorting the array:
-(void) processForTableView:(NSMutableArray *)items {
for (Patient *thisPatient in items) {
NSString *firstCharacter = [thisPatient.patientName substringToIndex:1];
if (![charIndex containsObject:firstCharacter]) {
[charIndex addObject:firstCharacter];
[charCount setObject:[NSNumber numberWithInt:1] forKey:firstCharacter];
} else {
[charCount setObject:[NSNumber numberWithInt:[[charCount objectForKey:firstCharacter] intValue] + 1] forKey:firstCharacter];
}
}
charIndex = (NSMutableArray *) [charIndex sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:#"cell"];
NSString *letter = [charIndex objectAtIndex:[indexPath section]];
NSPredicate *search = [NSPredicate predicateWithFormat:#"patientName beginswith[cd] %#", letter];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"patientName" ascending:YES];
NSArray *filteredArray = [[patients filteredArrayUsingPredicate:search] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
if ( nil == cell ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
NSLog(#"indexPath.row = %d, patients.count = %d", indexPath.row, patients.count);
Patient *thisPatient = [filteredArray objectAtIndex:[indexPath row]];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", thisPatient.patientName, thisPatient.patientSurname];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.textColor = [UIColor blackColor];
if (self.editing) {
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}
return cell;
}
I suspect this is quite a common thing that happens when you sort an array though it may not be.
Please say if you want any more code
It's not neccessarily because your array is sorted, but because the array you are populating your table from is not the same as the array that you are removing the object from.
Where are you sorting the patients array? Is the actual patients array being sorted or are you sorting it in your tableView delegates method and not actually sorting patients?
The reason for this is that the index of the object you deleted is not the same as the index that it has in the actual patients array (because one is sorted and one is not). Because of this, it is first deleting the wrong object, then it crashes because the tableView expects one to be deleted (so that it can animate that cell being removed) but the wrong one was deleted.

EXC BAD ACCESS after viewDidLoad in second viewController

The HDLogOnViewController passes two variables to the HDDomicileViewController in the
tableview:didSelectRowAtIndexPath method of HDLogOnViewController.
The app crashes with a EXC BAD ACCESS error after the viewDidLoad method of the HDDomicileViewController. The variables are verified correct in HDDomicileViewController.
I have enabled Zombies with no help. When I enable Guard Malloc the app runs normally. In the output view of XCode there is no indication of what is causing the error. I have researched many EXC BAD ACCESS threads here and have tried using properties instead of instance variables. I have used four if statements instead of if else if. When doing this the app would run normally with only one if statement but would crash with more than one. Also, with the four if statements I could comment out the statements of each and it would crash, making it appear the problem was in the if condition.
How can I discover what is causing the error?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellLabel = [NSString stringWithString:[[[HDLogOnStore sharedStore] cellTitleLabels] objectAtIndex:[indexPath row]]];
if ([cellLabel isEqualToString:#"Domicile"]) {
tableViewArray = [[HDLogOnStore sharedStore] domiciles];
tableViewArrayName = #"domiciles";
NSLog(#"indexPath row is %i", [indexPath row]);
NSLog(#"array name is %#", [[[HDLogOnStore sharedStore] cellTitleLabels] objectAtIndex:[indexPath row]]);
}
else if ([cellLabel isEqualToString:#"Position"]) {
tableViewArray = [[HDLogOnStore sharedStore] positions];
tableViewArrayName = #"positions";
NSLog(#"indexPath row is %i", [indexPath row]);
NSLog(#"array name is %#", [[[HDLogOnStore sharedStore] cellTitleLabels] objectAtIndex:[indexPath row]]);
}
else if ([cellLabel isEqualToString:#"BidRound"]) {
tableViewArray = [[HDLogOnStore sharedStore] bidRounds];
tableViewArrayName = #"bidRounds";
NSLog(#"indexPath row is %i", [indexPath row]);
NSLog(#"array name is %#", [[[HDLogOnStore sharedStore] cellTitleLabels] objectAtIndex:[indexPath row]]);
}
else if ([cellLabel isEqualToString:#"Month"]) {
tableViewArray = [[HDLogOnStore sharedStore] months];
tableViewArrayName = #"months";
NSLog(#"indexPath row is %i", [indexPath row]);
NSLog(#"array name is %#", [[[HDLogOnStore sharedStore] cellTitleLabels] objectAtIndex:[indexPath row]]);
}
HDDomicileViewController *domicileViewController = [[HDDomicileViewController alloc]init];
[domicileViewController setSelectedArray:tableViewArray];
[domicileViewController setSelectedArrayName:tableViewArrayName];
[self.navigationController pushViewController:domicileViewController animated:YES];
}
I began getting inconsistent behavior so I went back to reading more posts here.
I solved the problem by following a suggestion to correct all errors found after analyzing the project. I corrected a few seemingly unrelated errors such as "value stored to 'pairing' during it's initialization is never read". I am not sure if this directly solved the problem but somewhere in the process the problem went away.
FYI
The app was crashing after the viewDidLoad method and before the viewWillAppear method. Step by step debugging led me to machine code which I know nothing about.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"selectedArray count is %i",[selectedArray count]);
NSLog(#"selectedArray name is %#",selectedArrayName);
NSLog(#"checkedRowMonth is %i",[[HDLogOnStore sharedStore]checkedRowMonth]);
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}

uiTableView crashes the second time in deleting a cell

I'n new in iPhone, I'm trying to delete a cell from my UITableView, the first time it deletes well, but the second time it gives me the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Invalid update: invalid number of rows in section 0. The number of rows
contained in an existing section after the update (3) must be equal to the number
of rows contained in that section before the update (3), 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).'
here is my code of table:
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
Book_own *temp= (Book_own *)[self.books objectAtIndex:indexPath.row];
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[books removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
isDeleted = #"true";
deletedBook_id = temp.bo_id;
[self viewDidLoad];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSDictionary *dictionary = [listOfItems objectAtIndex:section];
NSArray *array = [dictionary objectForKey:#"myBooks"];
return [array count];
}
in ViewDidLoad I wrote the following code:
NSDictionary *mbooks = [NSDictionary dictionaryWithObject:books forKey:#"myBooks"];
NSDictionary *mgroups = [NSDictionary dictionaryWithObject:filteredParts forKey:#"myBooks"];
listOfItems = [[NSMutableArray alloc] init];
[listOfItems addObject:mbooks];
[listOfItems addObject:mgroups];
Can anyone tell me how to solve this issue??
Thanks in advance.
What is happening is that you are either not deleting the item or you are deleting only one item when you allow multible deleting. You can adjust by checking if it is really deleted as below or reload data if not:
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
BOOL success = [self removeFile:indexPath];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
if (!success) [tableView reloadData];
}
}
This method then removes the datasource item (it is from my own project so the names should be adjusted:
-(BOOL) removeFile:(NSIndexPath *)indexPath{
// Removes from the datasource and the filesystem
NSURL *fileURL = [self.dataSource objectAtIndex:indexPath.row];
NSError *error;
BOOL success = [[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];
if (success) {
[self.dataSource removeObjectAtIndex:indexPath.row];
} else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:[error localizedDescription] delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
[alert release];
[self.dataSource removeObjectAtIndex:indexPath.row];
}
return success;
}
If I'm reading your code correctly your data source is listOfItems. You should remove the row from your tables data source. A general rule is that when you remove or add items to a UITableView you must update the databsource.
[listOfItemsremoveObjectAtIndex:indexPath.row];
First, do not call [self viewDidLoad]; This method should not be called manually.
I think you are not calling the update method to your table view. This might fix your problems:
[tableView beginUpdates];
[books removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
edit: you also need to look at your code more closely. You are deleting a record from your datasource array directly from the row of the indexPath, which could be problematic considering the fact that your tableView has two sections.
The error says that there should be 1 fewer row after the deletion. It fails because the datasource code is not consistent in how it reports about the model.
Look at how you answer numberOfRowsInSection. Correctly, I think, first picking out the array that the section index represents, then answering that array's count.
The same logic must apply on the delete. The array you delete from needs to be the array indicated by the indexPath.section. Consider this:
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// the array you manipulate here must be the same array you use to count
// number of rows, and the same you use to render rowAtIndexPath
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"myBooks"];
// in order to be edited, it must be mutable
// you can do that on the fly here, or set it up as mutable
// let's make it temporarily mutable here:
NSMutableArray *mutableCopy = [array mutableCopy];
if (editingStyle == UITableViewCellEditingStyleDelete) {
[mutableCopy removeObjectAtIndex:indexPath.row];
// if it's setup mutable, you won't need to replace the immutable copy in the dictionary.
// but we just made a copy, so we have to replace the original
[listOfItems replaceObjectAtIndex:indexPath.section withObject:[NSArray arrayWithArray:mutableCopy]];
// and so on

Hiding a specific row of UITableViewCell

I am working on a todo list application with CoreData + UITableView, I would like to hide the row that the user mark as done.
My current solution is invoke deleteRowsAtIndexPaths when user mark the task done and deduct the deleted row from the function of numberOfRowsInSection.
-(void)markDone:(NSIndexPath *) _indexPath{
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:_indexPath] withRowAnimation:UITableViewRowAnimationFade];
deletedCount = deletedCount + 1;
[self.tableView endUpdates];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
if (deletedCount>0) {
return [sectionInfo numberOfObjects]-deletedCount;
}
return [sectionInfo numberOfObjects];
}
Although this method does work, but I do need some code hacking here and there. Is there a way to invoke NSFetchedResultsController didChangeObject for changing of status of particular field?
Thanks
I think there are many ways to solve this. I'd just add a field in your managed object which states if a row is hidden or not"
Deleting will set this field accordingly.
What you need now is an NSFetchRequest with the corresponding predicate to filter hidden rows.
I just created a simple template app with core data support, and I think it is very easy to achieve:
I added a hidden BOOL property to the one entitiy given, with default NO.
Then I added this code to didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[selectedObject setValue:[NSNumber numberWithBool:YES] forKey:#"hidden"];
}
In - (NSFetchedResultsController *)fetchedResultsController {... I added
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"hidden == NO"];
[fetchRequest setPredicate:predicate];
after
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
This was it to hide cells by clicking(just for this example) on them.

Core Data Error "Fetch Request must have an entity"

I've attempted to add the TopSongs parser and Core Data files into my application, and it now builds succesfully, with no errors or warning messages. However, as soon as the app loads, it crashes, giving the following reason:
UPDATE: I've got it all working, but my TableView doesn't show any data, and the app doesn't respond to the following breakpoints.
Thanks.
UPDATE: Here's the new code that doesn't respond to the breakpoints.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)table {
return [[fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (void)viewDidUnload {
[super viewDidUnload];
self.tableView = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCellIdentifier = #"SongCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.font = [UIFont boldSystemFontOfSize:14];
}
Incident *incident = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:NSLocalizedString(#"#%d %#", #"#%d %#"), incident.title];
return cell;
}
- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[table deselectRowAtIndexPath:indexPath animated:YES];
self.detailController.incident = [fetchedResultsController objectAtIndexPath:indexPath];
[self.navigationController pushViewController:self.detailController animated:YES];
}
UPDATE: Here's the code where all instances of fetch are found.
- (Category *)categoryWithName:(NSString *)name {
NSTimeInterval before = [NSDate timeIntervalSinceReferenceDate];
#ifdef USE_CACHING
// check cache
CacheNode *cacheNode = [cache objectForKey:name];
if (cacheNode != nil) {
// cache hit, update access counter
cacheNode.accessCounter = accessCounter++;
Category *category = (Category *)[managedObjectContext objectWithID:cacheNode.objectID];
totalCacheHitCost += ([NSDate timeIntervalSinceReferenceDate] - before);
cacheHitCount++;
return category;
}
#endif
// cache missed, fetch from store - if not found in store there is no category object for the name and we must create one
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:self.categoryEntityDescription];
NSPredicate *predicate = [self.categoryNamePredicateTemplate predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObject:name forKey:kCategoryNameSubstitutionVariable]];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
NSAssert1(fetchResults != nil, #"Unhandled error executing fetch request in import thread: %#", [error localizedDescription]);
Category *category = nil;
if ([fetchResults count] > 0) {
// get category from fetch
category = [fetchResults objectAtIndex:0];
} else if ([fetchResults count] == 0) {
// category not in store, must create a new category object
category = [[Category alloc] initWithEntity:self.categoryEntityDescription insertIntoManagedObjectContext:managedObjectContext];
category.name = name;
[category autorelease];
}
#ifdef USE_CACHING
// add to cache
// first check to see if cache is full
if ([cache count] >= cacheSize) {
// evict least recently used (LRU) item from cache
NSUInteger oldestAccessCount = UINT_MAX;
NSString *key = nil, *keyOfOldestCacheNode = nil;
for (key in cache) {
CacheNode *tmpNode = [cache objectForKey:key];
if (tmpNode.accessCounter < oldestAccessCount) {
oldestAccessCount = tmpNode.accessCounter;
[keyOfOldestCacheNode release];
keyOfOldestCacheNode = [key retain];
}
}
// retain the cache node for reuse
cacheNode = [[cache objectForKey:keyOfOldestCacheNode] retain];
// remove from the cache
[cache removeObjectForKey:keyOfOldestCacheNode];
} else {
// create a new cache node
cacheNode = [[CacheNode alloc] init];
}
cacheNode.objectID = [category objectID];
cacheNode.accessCounter = accessCounter++;
[cache setObject:cacheNode forKey:name];
[cacheNode release];
#endif
totalCacheMissCost += ([NSDate timeIntervalSinceReferenceDate] - before);
cacheMissCount++;
return category;
}
And this one...
- (void)fetch {
NSError *error = nil;
BOOL success = [self.fetchedResultsController performFetch:&error];
NSAssert2(success, #"Unhandled error performing fetch at SongsViewController.m, line %d: %#", __LINE__, [error localizedDescription]);
[self.tableView reloadData];
}
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Song" inManagedObjectContext:managedObjectContext]];
NSArray *sortDescriptors = nil;
NSString *sectionNameKeyPath = nil;
if ([fetchSectioningControl selectedSegmentIndex] == 1) {
sortDescriptors = [NSArray arrayWithObjects:[[[NSSortDescriptor alloc] initWithKey:#"category.name" ascending:YES] autorelease], [[[NSSortDescriptor alloc] initWithKey:#"rank" ascending:YES] autorelease], nil];
sectionNameKeyPath = #"category.name";
} else {
sortDescriptors = [NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:#"rank" ascending:YES] autorelease]];
}
[fetchRequest setSortDescriptors:sortDescriptors];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:sectionNameKeyPath cacheName:#"SongsCache"];
}
return fetchedResultsController;
}
your extra caching is probably a waste of cycles as Core Data performs its own caching internally. I am willing to bet you are slowing things down rather than speeding them up, not to mention the additional memory you are consuming.
Where are you setting categoryEntityDescription? That is now shown in the code you posted. It is probably nil.
Why are you retaining an NSEntityDescription?!? They are already in memory because of Core Data and retaining them is a waste which could lead to issues if Core Data wants to release it at some point.
update
Your caching is definitely not coming from Apple's code because they know that the cache is in Core Data.
As for the NSEntityDescription, again, do not retain the NSEntityDescription.
Are you 100% positive that the NSEntityDescription is not nil? Have you confirmed it in the debugger? Have you tested it with a freshly retrieved NSEntityDescription?
update
You need to learn to use the debugger as that will solve most of your coding issues. Put a breakpoint in this method and run your code in the debugger. Then when the execution stops on that break point you can inspect the values of the variables and learn what they are currently set to. That will confirm or deny your suspicions about what is and is not nil.
This error you are seeing happens when you fail to set the Entity in the NSFetchRequest which, based on your code, means that retained property is not being set before the code you have shown is being called.
Based on the code posted and the problem description, I suspect that the categoryEntityDescription property is returning nil.
I've seen this happen when the NSEntityDescription given to a fetch request is nil. The most likely cause of that is that you have a model entity that is named differently from the name you provided to entityForName. Barring that, it could be an error in configuration of your Core Data stack or a missing data model, but as a first step, I would recommend storing the result of entityForName in a local variable and breaking there to make sure it isn't nil.
Since you added the model file manually, is the .xcdatamodel file inside the Compile Sources step in your Target?
Go to the Targets entry in the Groups & Files pane in Xcode and click the disclosure triangle. Then click on the disclosure triangle for your app. Then check to see if it's in Compile Sources. If not, right click on Compile Sources and choose "Add -> Existing File..." and add it.
Edit based on update:
UPDATE: Here's the new code that
doesn't respond to the breakpoints.
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Is your view controller set as the UITableViewDataSource/UITableViewDelegate for your UITableView? If not, these methods will not get called.