Mogenerator with NSFetchedResultsController - 0 sections - objective-c

I am populating a UITableView from Core Data using an NSFetchedResultsController. The data is coming from an NSManagedObject subclass generated by Mogenerator called MenuItem. The MenuItem entity has a SectionID parameter that is an NSNumber and this is used to determine what section the item should be in within the table view.
I performed a test fetch on the data and confirmed that Core Data was populated correctly. All was fine.
The NSFetchedResultsController is created as follows and the sectionKeyNamePath is set to #"sectionId":
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MenuItem" inManagedObjectContext:self.persistenceController.moc];
[fetchRequest setEntity:entity];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"sectionId" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:#"rowId" ascending:YES]];
NSFetchedResultsController *frc = nil;
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceController.moc
sectionNameKeyPath:#"sectionId"
cacheName:nil];
The problem (and solution):
With this code, the sections are not identified. The NSFetchedResultsController always returns 0. This worked previously when I created the NSMO subclass manually, so I figured it was something related to Mogenerator.
If I change the sectionNameKeyPath to be #"primitiveSectionId" then it works.
Hopefully this will help somebody in the future, but I don't understand what is going on here. Please could somebody explain why this fixes the problem?
Thanks

Why are you setting the FRC to nil? Try this (it works for me using an NSNumber Attribute):
With mogenerator you can get the entity using "[MenuItem entityName]" and access attributes using MenuItemAttributes.sectionId, for example.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[MenuItem entityName]];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:MenuItemAttributes.sectionId ascending:YES], [NSSortDescriptor sortDescriptorWithKey:MenuItemAttributes.rowID ascending:YES]];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceController.moc
sectionNameKeyPath:MenuItemAttributes.sectionId
cacheName:nil];
// Make sure you set your frc as the delegate
frc.delegate = self;
NSError *error = nil;
if (![fetcher performFetch:&error]) {
/*
* Did you execute the request?
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"%lu", [frc.sections count]);

Related

Memory Management Issue with CoreData

Note: This question has been completely re-organized due to so many down votes.
I am displaying a UITableViewController using NSFetchedResults with core data.
It displays fine, but when I push a new view controller and the user uses the back button to navigate back to the UITableView its crashes with that error.
I have been battling this for weeks and have not figured out why. I can tell that its likely related to memory management but I cant find it anywhere.
Update: Using Instruments-Zombies I found out that in fact it was crashing on CoreData -prepareResultsForResultsSet. See image
Here is the code from my FetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController {
NSManagedObjectContext *thecontext;
thecontext = [(Logix_AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TimeOff" inManagedObjectContext:thecontext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"timeOffStartDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:thecontext sectionNameKeyPath: #"timeOffType" cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
fetchedResultsController.delegate = self;
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return fetchedResultsController;
}
Can you copy/paste the actual error from Xcode? And backtrace of where the crash happens, if any?
You definitely should not be autoreleaseing the object returned by +imageNamed:; that object is already autoreleased.
Your comment:
iVacationTimeApp[47892:19d03] -[CFString respondsToSelector:]: message sent to deallocated instance 0x95c9140
There is your answer. You have an overreleased object. May be the image, might be something else. Turn on Zombie detection in Instruments and run again.

NSFetchedResultsController has 0 sections

I'm having a problem I don't know where it comes from, related to CoreData. In my database, a have a set of categories (with name and description), which contain elements (using a one-to-many relationship).
I want to divide my table view in sections given an attribute of the Category class, but when I try to do it using sectionNameKeyPath:, the resulting NSFetchedResultsController has 0 sections. If I pass nil to this parameter, it has 1 section.
The code is the following:
- (NSFetchedResultsController*) fetchedResultsController
{
if(fetchedResultsController)
return fetchedResultsController;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Category"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:10];
// Edit the sort key as appropriate.
NSSortDescriptor *checkDescriptor = [[NSSortDescriptor alloc] initWithKey:#"checked"
ascending:YES];
NSSortDescriptor *indexDescriptor = [[NSSortDescriptor alloc] initWithKey:#"orderIndex"
ascending:YES];
NSArray *sortDescriptors = #[checkDescriptor, indexDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"checked"
cacheName:nil];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
return nil;
} else {
fetchedResultsController.delegate = self;
return fetchedResultsController;
}
}
See the NSFetchedResultsController documentation: The key used for sectionNameKeyPath ("name" in your case) must be the same key used in the first sort descriptor ("checked" in your case). They can be different, but then both keys must generate the same relative ordering.
In your case I assume that you want to add an additional sort descriptor on "name" and use that as the first sort descriptor.

NSFetchedResultsController not sorting on initial fetch

I am having a Core Data problem with NSFetchedResultsController. I have a one to many relationship between a parent and child entity. The array in the childFetchedResults.fetchedObjects property is NOT sorted by number (number is an int32 property in the model). It doesn't seem to matter if I use the MagicalRecord convenience category methods or not.
NSFetchRequest *req = [Child MR_requestAllSortedBy:#"number" ascending:YES withPredicate:[NSPredicate predicateWithFormat:#"parent = %#", self.parent]];
childFetchedResults = [[NSFetchedResultsController alloc] initWithFetchRequest:req managedObjectContext:[NSManagedObjectContext MR_defaultContext] sectionNameKeyPath:nil cacheName:nil];
childFetchedResults.delegate = self;
NSError *error;
[childFetchedResults performFetch:&error];
NSLog(#"fetched objects: %#", childFetchedResults.fetchedObjects);
However, if I sort the array of fetched objects using the exact same sort descriptor, it works fine:
NSLog(#"fetched objects: %#", [childFetchedResults.fetchedObjects sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"number" ascending:YES]]]);
I gather you can only use comparators which Core Data can pass on to the SQLite store when specifying sort descriptors for a fetch request. But I feel like that shouldn't matter in this case since sorting by a number has got to be supported by SQLite.
Anyone solved this? I feel like it's a similar issue to the one described here: NSSortDescriptor not being called.
As for MR_requestAllSortedBy, it's in MagicalRecord, here is the implementation:
+ (NSFetchRequest *) MR_requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self MR_requestAllInContext:context];
NSSortDescriptor *sortBy = [[NSSortDescriptor alloc] initWithKey:sortTerm ascending:ascending];
[request setSortDescriptors:[NSArray arrayWithObject:sortBy]];
return request;
}
So this was caused by fetching against a nested MOC with unsaved changes. Either fetching with the parent MOC or saving the nested MOC prior to executing the fetch resolves the problem. Similar to what was going on in this question: NSSortdescriptor ineffective on fetch result from NSManagedContext
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"RemainderDataBase" inManagedObjectContext:[self managedObjectContext]];
NSSortDescriptor *nameSort = [[NSSortDescriptor alloc]initWithKey:#"title" ascending:YES];
NSArray *sortDescriptor = [[NSArray alloc]initWithObjects:nameSort, nil];
fetchRequest.sortDescriptors = sortDescriptor;
NSPredicate *p1=[NSPredicate predicateWithFormat:#"startdate > %#", [NSDate date]];
[fetchRequest setPredicate:p1];
[fetchRequest setEntity:entity];
i think you are looking for the above code..

What's the benefit of this coding style?

Recently I came across this Objective-C coding style:
- (NSFetchedResultsController *)fetchedResultsController
{
NSFetchRequest *fetchRequest;
NSEntityDescription *entity;
NSSortDescriptor *sortDescriptor;
NSArray *sortDescriptors;
NSError *error;
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
entity = [NSEntityDescription entityForName:#"Deck" inManagedObjectContext:self.managedObjectContext];
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES];
sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
DLog(#"Failed fetching decks: %#, %#", [error localizedDescription], [error userInfo]);
}
// etc...
The thing I'm talking about are the declarations at the top of the method. What's that for? Is it good practice? It seems to add some clarity to the code, as you can instantly see what variables the method will use, right?
On one hand, it adds clarity as you immediately see what variables the method will use. It is a legacy from C where you had to declare all the variables at the top of a brackets block as no declarations were allowed in the middle of a block.
On the other hand, one may argue that the variable declarations are sometimes far from the place where they will be used for the first time, so it does not help understanding what a variable will be used for. I think it's just a matter of taste.
I can't imagine a benefit from "immediately seeing what variables the method will use", while I see great benefits in declaring variables where they are used, and initializing them on the same line as the declaration.
This style is no longer required and has fallen out of use. However, I could see some benefit if this group of declarations at the top of a method are followed by a corresponding group of releases at the bottom, but as others have pointed out, it comes at the cost of readability.

NSFetchedResultsController always returns NSManagedObject objects instead of custom

I am using CoreData and have an Entity ContactList defined, which I created using the XCode xcdatamodel tool (or whatever you call that built-in thing:). I also set the Entity's Class name to "ContactList". I then used rentzsch's mogenerator (http://github.com/rentzsch/mogenerator) to generate my custom class files and added the simple method "toString" to my ContactList class.
The fetchedResultsController callback is pretty much standard and looks as follows:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController_ != nil) {
return fetchedResultsController_;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ContactList" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController_;
}
Now, when I start my application and try to call that method on a "ContactList" object, which in turn I get from a NSFetchedResultsController, I get
ContactList *contactList = (ContactList *) [self.fetchedResultsController objectAtIndexPath:indexPath];
[contactList toString];
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject toString]: unrecognized selector sent to instance 0x6af7bf0'
As the message shows, somehow the NSFetchedResultsController does not return a "ContactList" object, but a more general NSManagedObject instead.
Does anybody know why and how to resolve this???
I had this same problem - Caused because I had previously renamed the Entity's Managed Object subclass. In the Data Modeler, make sure in the Inspector for the entity, both the "Name" and the "Class" are set correctly.
Ok, I figured this it out.
The problem was that the mogenerator script did not add its generated source files to the project's compile target. This way the classes never got compiled and included (I wonder how it worked without any model object classes :)
After adding the .xcdatamodel file to the Target (RightClick on .xcdatamodel file -> Targets -> check your project) everything works now.
For further info go here