I am trying to resolve this for days at this stage and I'm hoping you can help.
I have two ViewControllers which query two different tables from the same database using Core Data. The first ViewController is opened with the app and displays fine. The second is called from within the first ViewController, using a pretty standard fetch setup:
- (NSFetchedResultsController *)fetchedClients {
// Set up the fetched results controller if needed.
if (fetchedClients == nil) {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Clients"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"clientsName" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedClients = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
}
return fetchedClients;
}
When I call [self.fetchedClients sections], I get a nil (0x0) return.
I have examined the database using an external application to ensure data exists in the "Clients" table.
Are you compiling against 3.0? Thee is a bug in 3.0 that was fixed in 3.1 that can cause this issue.
update
Are you calling -performFetch: on the returned NSFetchedResultsController? I dont't see it in the code you posted.
Related
Hi I have following issue:
I'm programming a game and everytime the player finishes a score is submitted. So i got two Datasources: Score and Mode. And i want to sort these Highscores in 4 sections, the 4 modes i have. And in these sections it should be sorted by Score (highest on the top).
Yet I got this code:
- (NSFetchedResultsController *)fetchedResultsController{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"score" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"mode" cacheName:nil];
_fetchedResultsController.delegate = self;
[_fetchedResultsController performFetch:nil];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;}
But all I get is a pretty random Sorting. The first time it worked but after a few games it messed up!
My modes are: 10,20,30,60 (seconds) The golden badge on the left shows what section it should be sorted in.
Hope someone can help me.
iPhone Screenshot
You need to add another sort descriptor for mode as well as for score.
You need to sort first by mode and then by score. As you are currently sorting by score and sectioning by name you'll get...
Mode 1:
- Score 100
- Score 99
Mode 2:
- Score 80
Mode 1:
- Score 70
etc...
Change your sort descriptor code like this...
NSSortDescriptor *modeSD = [[NSSortDescriptor alloc] initWithKey:#"mode" ascending:YES];
NSSortDescriptor *scoreSD = [[NSSortDescriptor alloc] initWithKey:#"score" ascending:NO];
NSArray *sortDescriptors = #[modeSD, scoreSD];
[fetchRequest setSortDescriptors:sortDescriptors];
That should sort it for you (pun very much intended ;-) )
If you would like four separate sections then your NSFRC will look like this...
// note the section name key path
NSFetchedResultsController *nsfrc = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"mode" cacheName:nil];
This will then split the data into sections and rows.
Count of sections is [[nsfrc allSections] count];
And so on...
I have a core data application which displays a list of custom objects, each representing theatrical productions.
Each show object has a bunch of properties - name, logo, opening date, show type.
I can retrieve them fine, and sort by type just fine - but I would like each show type to be its own section in the table.
I don't know ahead of time how many show types will be represented in the result set, so I don't know initially what the numberofsections should be.
So I guess the question is - how would I go about dividing the result set into sections grouped by type?
Currently I'm doing this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Show" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"type" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
I would write numberOfSections method so that it dynamically checks for the number of types. Then, whenever a new type rolls in, just do a [tableView reloadData];
I am working with a UITableView that gets its data from an NSFetchedResultsController. I would like to add a UISegmentedControl to my navigation bar that would toggle the table between displaying all of the records and only the records where starred == YES.
I have read some other SO posts indicating that one way to do this is to create a second NSFetchedResultsController that has an NSPredicate with starred == YES, but it seems awfully overkill to create a second NSFetchedResultsController.
Is there a simpler way to do this?
Not according to the docs on NSFetchedResultsController. If you take a look at the documentation on the fetchRequests property, you'll see the following note:
Important: You must not modify the
fetch request. For example, you must
not change its predicate or the sort
orderings.
Since the fetchRequest property is read-only, the only option is creating a new fetched results controller.
It might be possible to change the predicate and have things work, but it's generally a bad idea to do stuff that goes explicitly against things Apple says in the documentation, because it could break in a future release.
And, beware of premature optimization! Unless you've tested it out and found out that creating a whole new fetched results controller is a big performance drain, it's not worth trying to do something in a non-recommended way.
Here's how I set a new predicate on my fetched results controller. fetchedResultsController is a property of my view controller. predicate is a private ivar of the view controller.
I already had all of the code for creating the fetched results controller on demand, so to set the predicate it's just a matter of deleted the cached one.
- (void)setPredicate:(NSPredicate *)newPredicate {
predicate = [newPredicate copy];
// Make sure to delete the cache
// (using the name from when you created the fetched results controller)
[NSFetchedResultsController deleteCacheWithName:#"Root"];
// Delete the old fetched results controller
self.fetchedResultsController = nil;
// TODO: Handle error!
// This will cause the fetched results controller to be created
// with the new predicate
[self.fetchedResultsController performFetch:nil];
[self.tableView reloadData];
}
This code if based on the boilerplate XCode generates when you start a project that uses Core Data.
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:[MyEntity entityName] inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setPredicate:predicate];
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
To preface, this is a follow up to an inquiry made a few days ago:
https://stackoverflow.com/questions/2981803/iphone-app-crashes-when-merging-managed-object-contexts
Short Version: EXC_BAD_ACCESS is crashing my app, and zombie-mode revealed the culprit to be my predicate embedded within the fetch request embedded in my Fetched Results Controller. How does an object within an object get released without an explicit command to do so?
Long Version:
Application Structure
Platforms View Controller -> Games View Controller (Predicated upon platform selection) -> Add Game View Controller
When a row gets clicked on the Platforms view, it sets an instance variable in Games View for that platform, then the Games Fetched Results Controller builds a fetch request in the normal way:
- (NSFetchedResultsController *)fetchedResultsController{
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
//build the fetch request for Games
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Game"
inManagedObjectContext:context];
[request setEntity:entity];
//predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"platform == %#",
selectedPlatform];
[request setPredicate:predicate];
//sort based on name
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
//fetch and build fetched results controller
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[sortDescriptor release];
[sortDescriptors release];
[predicate release];
[request release];
[aFetchedResultsController release];
return fetchedResultsController;
}
At the end of this method, the fetchedResultsController's _fetch_request -> _predicate member is set to an NSComparisonPredicate object. All is well in the world.
By the time - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section gets called, the _predicate is now a Zombie, which will eventually crash the application when the table attempts to update itself.
I'm more or less flummoxed. I'm not releasing the fetched results controller or any of it's parts, and the only part getting dealloc'd is the predicate. Any ideas?
EDIT:
As a test, I added this line to the Fetched Results Controller method:
[fetchedResultsController.fetchRequest.predicate retain];
And now it doesn't crash, but that seems like a patch, not something I should be doing.
You shouldn't be releasing your predicate variable. You didn't invoke new, alloc, retain, or copy (This is the "narc" rule) to create the predicate, so you are not responsible for releasing it. That's where your zombie is coming from.
I'm trying to acces a property from a managedobject relationship in one of my entity but i keep getting a "nil" object from it. Here's a summary of the entities and there relation.
Entity: House
Relationships: adress (1 to 1)
Entity: Adresse
Properties: street (NSString)
Relationships: house (1 to 1)
So basically a "House" can only have one "Adresse". I want to be able to sort by the street property from the adress relationship when i fetch a House. Here's the code that i'm using but can't seem to make it work out. What am i missing?
- (NSFetchedResultsController *)fetchedResultsController {
// Set up the fetched results controller if needed.
if (fetchedResultsController == nil) {
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = [appDelegate managedObjectContext];
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"House" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Edit the sort key as appropriate.
NSString *sectionKey = #"adresse.rue";
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sectionKey ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:sectionKey cacheName:#"House"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
}
return fetchedResultsController;
}
Your code looks OK. Your fetch request is valid. If it's not throwing any exceptions, then your key path is specified properly.
I'm not quite sure what you mean by "getting a nil object from it". If you mean your fetched results controller doesn't contain any objects, make sure that you're calling performFetch: before you try accessing any objects. The controller will be empty until you do this.
Does this code work when you leave out the sort descriptors and key path?