What's the benefit of this coding style? - objective-c

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.

Related

Mogenerator with NSFetchedResultsController - 0 sections

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

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.

Simple NSPredicate #"1 == 0" not filtering fetched results?

What is wrong with this code?
I know this is a silly predicate. But it's just to show that if this was working, it should be filter everything out, right?
Somehow, it's not. I'm getting every Month object in my DB, when I should be getting none.
I guess this tells me that the problem might not be in the predicate?
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Month" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate: [NSPredicate predicateWithFormat:#"1 == 0"]];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"month_" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:#"month_"
cacheName:#"Root"];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[_fetchedResultsController sections] count];
}
What I actually wanted to do is,
[NSPredicate predicateWithFormat:#"year.year_ == %d", year]
And I also tried this,
[NSPredicate predicateWithFormat:#"month_ == %d", 1]
Just to see if it works. But nothing.
edit:
At this point, no matter what predicate I set, as long it's a valid, well formatted predicate. It doesn't complaint, and get's me all objects. It's just not filtering anything.
I'm pretty much using boiler plate code from Apple's documentation.
Can anyone help me figure this out?
Thank you!
If using a cache, then the fetchRequest's cache should be deleted, before being reused.
Calling deleteCacheWithName: fixed the problem.

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 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