Core Data Memory Leaks - iphone

I began testing my app in Instruments to clean up any memory leaks. I've been able to clear up all the memory leaks except those related to Core Data. Instruments always points me to this section of code:
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error
}
I declare the managedObjectContext in my header file with the following code:
#interface UpperBody : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *upperTable;
NSMutableArray *exercises;
NSManagedObjectContext *managedObjectContext;
}
#property (nonatomic, retain) NSMutableArray *exercises;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
I release the managedObjectContext in the (void)dealloc section. Here is the full section of code using the managedObjectContext:
- (void)loadExercises {
if (managedObjectContext == nil) {
managedObjectContext = [(iFitAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Exercises" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"category == 1"];
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"exerciseName" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error
}
[self setExercises:mutableFetchResults];
// [exercises addObject:#"Add Exercise"];
NSLog(#"Count of exercises %i", exercises.count);
[mutableFetchResults release];
[request release];
[self.tableView reloadData];
}
Any advise on what might be causing the leaks would be greatly appreciated! Thank you in advance!

kThere was probably a leak in the code I had above. I got around declaring an NSManagedObjectContext by just using a pointer to one whenever needed. Here is a sample of the code:
iFitAppDelegate *appDelegate = (iFitAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
This fixed my leak, so it must have had to do with how I was allocated and releasing the NSManagedObjectContext. Thank you for the pointers, #albertamg!

Related

Core data sort data by two Descriptor

I have used core data in my ios app. Now i have one table in which there are two columns .
Category
Order (In which there are NSNumber 1, 2 ,5)
I want to fetch data so It will first sort alphabetically by Category name and than by Order Number.
I have used below code :
NSEntityDescription *entity_Maintness = [NSEntityDescription
entityForName:#"Maintness" inManagedObjectContext:__managedObjectContext];
[fetchRequest_Maintness setEntity:entity_Maintness];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"type" ascending:YES];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"serialNumber" ascending:NO];
NSArray *sortDescriptors12 = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor, nil];
[fetchRequest_Maintness setSortDescriptors:sortDescriptors12];
But the data are sorted only by Category not by Serial Number.
Thanks for Help.
The code above looks ok. Maybe there is an issue elsewhere in your code? Here is some code I quickly generated for you that may serve as starting point??
#import "MainViewController.h"
#import "Maintness.h"
#interface MainViewController ()
#property (weak, nonatomic) IBOutlet UITextField *type;
#property (weak, nonatomic) IBOutlet UITextField *serialNumber;
#property (strong, nonatomic) NSManagedObjectContext *context;
- (IBAction)addButtonPressed:(id)sender;
- (IBAction)retrieveButtonPressed:(id)sender;
#end
#implementation MainViewController
#pragma mark - lazy instantiation
- (NSManagedObjectContext *)context{
if (!_context){
id appDelegate = (id)[[UIApplication sharedApplication] delegate];
_context = [appDelegate managedObjectContext];
}
return _context;
}
#pragma mark - core data interactions
- (IBAction)addButtonPressed:(id)sender {
NSError *error = nil;
Maintness *newMaintness = nil;
newMaintness = [NSEntityDescription insertNewObjectForEntityForName:#"Maintness" inManagedObjectContext:self.context];
newMaintness.type = self.type.text;
newMaintness.serialNumber = self.serialNumber.text;
if (![self.context save:&error]) {
NSLog(#"Oh no - error: %#", [error localizedDescription]);
} else {
NSLog(#"It appears the details were added ok");
}
}
- (IBAction)retrieveButtonPressed:(id)sender {
NSError *error = nil;
// Set up fetch
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
// Set up entity
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Maintness" inManagedObjectContext:self.context];
[fetch setEntity:entity];
// Set up sorting
// - sorts in order of array
NSSortDescriptor *primarySortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"type" ascending:YES];
NSSortDescriptor *secondarySortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"serialNumber" ascending:NO];
NSArray *sortDescriptors = #[primarySortDescriptor, secondarySortDescriptor];
[fetch setSortDescriptors:sortDescriptors];
NSArray *results = [self.context executeFetchRequest:fetch error:&error];
for (Maintness *result in results) {
NSLog(#"type: %# serial number: %#", result.type, result.serialNumber);
}
}
#end

Completion block/callback on FetchRequest completion

I have a FetchRequest in my model class named ContentManager. It fetches quite a lot of data, so the screen is blank until it's completion. In the ViewController that displays the fetched results, I would like to show a loading indicator, so I would like to get a callback when the FetchRequest has completed and pass that to the ViewController to stop the loading indicator. Is this possible?
Here is the FetchRequest from the ContentManager class:
- (NSArray*)recipesForMagazine
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Recipe" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSString* path = [[NSBundle mainBundle] pathForResource:#"magazine_recipe_guids" ofType:#"plist"];
NSArray* recipeGuids = [NSArray arrayWithContentsOfFile:path];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"guid IN %#",recipeGuids];
[request setPredicate:predicate];
NSSortDescriptor* sort = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
[sort release];
NSError* error = nil;
NSArray* fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
[request release];
return fetchResults;
}
Here I set it up in the ViewController
self.magazineRecipes = [[ContentManager defaultManager] recipesForMagazine];
I want to set up the fetchrequest method like this:
- (NSArray*)recipesForMagazine:(void (^)(BOOL success))block or something, so in the viewcontroller I can call it like this
self.magazineRecipes = [[CTContentManager defaultManager] recipesForMagazine:^(BOOL success) {
if (success) {
//stop activity indicator view
}
}];
I don't know if I'm at all in the right way of thinking, thanks for your help in advance!
I would make the viewController a delegate of the ContentManager class. So in the ContentManager.h I would do something like:
#protocol ContentManagerDelegate()
-(void) didFetchResults:(NSArray *) results;
-(void) didResultsFail: (NSError *) error;
#end
#interface ContentManager : <SuperClass Name>
-(id) initWithDelegate: (id<ContentManagerDelegate>) delegate;
#property (nonatomic, strong) id<ContentManagerDelegate> delegate;
#end
and in the implementation:
-(id) initWithDelegate: (id<ContentManagerDelegate>) delegate
{
self = [super init];
if(self)
{
_delegate = delegate;
}
return self;
}
and in your recipesForMagazine method you can use the delegate [_delegate didFetchResults: fetchResults], you can implement a method to pass errors to the delegate if you want as well. In your ViewController.h do #interface ViewController.h : UIViewController <ContentManagerDelegate> and in the implementation you should be able to implement the didFetchResults: method that will give you the results and in that method you can stop the activity indicator from animating.

problem with NSSortDescriptor .i am sorting the in ascending order(alphabetical order)

I am using NSSortDescriptor to sort the custom objects with key bandName, but I am not getting the output in alphabetical order. How do I fix that?
-(void)getdetails
{
NSLog(#"in getdetail method");
SongRequestAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context1 = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"AddToFav" inManagedObjectContext:context1];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSError *error;
NSMutableArray *array_new=[[NSMutableArray alloc]init];
[array_new addObjectsFromArray:[context1 executeFetchRequest:request error:&error]];
//[self.fetchedObjects addObjectsFromArray:[context1 executeFetchRequest:request error:&error]];
[request release];
**NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"bandName" ascending:YES] autorelease];
NSMutableArray *sortDescriptors = [NSMutableArray arrayWithObject:sortDescriptor];
//self.fetchedObjects=
[array_new sortedArrayUsingDescriptors:sortDescriptors];
[self.fetchedObjects addObjectsFromArray:array_new];
[array_new release];**
for (int i=0; i<[self.fetchedObjects count];i++)
{
NSManagedObject
*addfav=[self.fetchedObjects objectAtIndex:i];
addFavObject *addfavobject=[[addFavObject alloc]init];
addfavobject.bandImagePath=[addfav valueForKey:#"bandImagePath"];
addfavobject.bandId=[addfav valueForKey:#"bandId"];
addfavobject.bandName=[addfav valueForKey:#"bandName"];
addfavobject.songId=[addfav valueForKey:#"songId"];
addfavobject.songListId=[addfav valueForKey:#"songListId"];
addfavobject.songName=[addfav valueForKey:#"songName"];
addfavobject.bandRealName=[addfav valueForKey:#"bandRealName"];
addfavobject.biography=[addfav valueForKey:#"biography"];
NSLog(#"addto fav object biography is %#",addfavobject.biography);
[self.addTofavObjectArray addObject:addfavobject];
[addfavobject release];
}
//NSArray *reverse = [[self.addTofavObjectArray reverseObjectEnumerator] allObjects];
// [self.addTofavObjectArray removeAllObjects];
// [self.addTofavObjectArray addObjectsFromArray:reverse];
NSLog(#"self.addTofavObjectArray is %#",self.addTofavObjectArray);
NSLog(#"FETCHED OBJECTS count is %d",[self.fetchedObjects count]);
}
sortedArrayUsingDescriptors: doesn't sort in place the array it was sent to. It returns a new array with the objects sorted. It should work with:
NSArray *sortedArray = [array_new sortedArrayUsingDescriptors:sortDescriptors];
[self.fetchedObjects addObjectsFromArray:sortedArray];
You don't need to do the sort separately like that though. You can give the sort descriptors to your fetch request and the array returned from executing it will be sorted. I'd highly recommend doing it this way instead of how you're doing it. So you can replace everything before the for loop with:
SongRequestAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context1 = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"AddToFav" inManagedObjectContext:context1];
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"bandName" ascending:YES] autorelease];
NSMutableArray *sortDescriptors = [NSMutableArray arrayWithObject:sortDescriptor];
[sortDescriptor release];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
[request setSortDescriptors:sortDescriptors];
NSError *error;
NSMutableArray *array_new = [context1 executeFetchRequest:request error:&error];
if (!array_new)
{
NSLog(#"error: %#", error);
}
[self.fetchedObjects addObjectsFromArray:array_new];
[request release];
I also added in the check for array_new to print the error. You might want to add additional error handling there too.

Can't Release NSFetchedResultsController in dealloc

I have two UITableViewControllers with a fairly simple ui flow. One UITableViewController loads another UITableViewController when you select an item in the first UITableViewController.
(UITableViewController) List of Stories -> Select a Story -> (UITableViewController) List of Sentences
In the second UITableViewController (MakeSentenceDetailViewController) I can't release my NSFetchedResultsController without causing an error (shown with Zombies set to on):
-[NSFetchRequest release]: message sent to deallocated instance 0x5b370f0
The retain count of the NSFetchedResultsController stays at 1 but when I try to release it in dealloc I get a crash.
The code, especially in regards to the NSFetchedResultsController is the same in both tableviews, but in the MakeSentenceDetailViewController I can't release this NSFetchedResults Controller with a crash - giving me a leak.
How can I safely release my NSFetchedResultsController? Why does it work fine in the parent (first) tableviewcontroller - but not in the second?
I can provide code for the first UITableViewController but in regards to NSFetchedResultsController it's declared and used in much the same way.
MakeSentenceTableViewController.h:
#interface MakeSentenceTableViewController : UITableViewController {
NSManagedObjectContext *managedObjectContext;
NSFetchedResultsController *fetchedResultsController;
}
#property (nonatomic, retain) Story *story;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#end
MakeSentenceTableViewController.m (relevant code with NSFetchedResultsController):
- (void)viewDidLoad {
[super viewDidLoad];
if (managedObjectContext == nil)
{
managedObjectContext = [(MyAppAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Sentence" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
//sorting stuff:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"order" ascending: YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
//[request setFetchBatchSize:FETCH_BATCH_SIZE];
[sortDescriptors release];
[sortDescriptor release];
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request managedObjectContext:managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
[request release];
NSError *error;
[fetchedResultsController performFetch:&error];
NSLog(#"FetchedResultsController: %#", fetchedResultsController);
NSLog(#"fetchedResultsController RetainCount at viewDidLoad: %d", [fetchedResultsController retainCount]);
}
- (void)dealloc {
//Gotta figure out why I can't release this:
[fetchedResultsController release]; //Crash! Burn!
NSLog(#"fetchedResultsController RetainCount at dealloc: %d", [fetchedResultsController retainCount]);
[managedObjectContext release];
[super dealloc];
}
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
// ...snip...
[request release];
You're releasing an object that you've relinquished ownership of (with -autorelease). You don't get an error at the point of the other release because the NSFetchedResultsController is also retaining the fetch request; thus the controller is the one actually causing the crash when it releases the last reference to the fetch request.
You are over-releasing the NSFetchRequest
You autorelease it here:
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
then release it again later:
[request release];
then later when you release the fetchedResultsController, it tries to release that same request again.

Example of how to make a data factory for core data access in cocoa (iPhone)?

I have been slowly learning iPhone development and seem to keep hitting walls where I can't figure out how to do what I want to do the right way :(
Basically, I want a class that handles all interactions with the data layer, for example, getting a mutable array of some list of objects from the data store.
This is pretty trivial in other languages where you have a garbage collector, but in Objective-C on the iPhone, I'm not sure what to do.
This is an example method on a DataFactory class we were creating. Note the comment on where we are not sure when to release....
- (NSMutableArray*)fetchAllDrivers{
NSMutableArray *results = [[NSMutableArray alloc] init];;
if (self.appDelegate != nil) {
NSManagedObjectContext *context = [self.appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
[request setEntity: entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setSortDescriptors: sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error;
results = [[context executeFetchRequest:request error:&error] mutableCopy];
if (results == nil) {
//something went wrong
}
//Where should this be released??? Certainly not here!
[results release];
[request release];
}
else {
[NSException raise:#"Can't fetch b/c app delgate is nil!" format: #"!!!"];
}
return results;
}
Calling code, related to my comment:
NSMutableArray* arr = [dataFactory fetchAllDrivers];
[arr retain];
//Some code where we use arr
[arr release];
Following naming conventions, your fetchAllDrivers should return an autoreleased object.
- (NSMutableArray*)fetchAllDrivers
{
if (!self.appDelegate) {
// Big Problems Raise exception immediately if you want...
return nil;
}
NSManagedObjectContext *context = [self.appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
[request setEntity: entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setSortDescriptors: sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error = nil;
NSMutableArray *results = [[NSMutableArray alloc] initWithArray:[context executeFetchRequest:request error:&error] copyItems:YES];
if (error) {
// Something went wrong
[results release];
// Error handling code here
[request release];
return nil;
}
[request release];
return [results autorelease];
}
NSMutableArray* arr = [dataFactory fetchAllDrivers];
[arr retain];
//Some code where we use arr
[arr release];
By convention, any object returned from the method of an external object is autoreleased. You don't need to retain them except in properties. If you only using arr in the local scope of the method then you don't need to retain/release it. It is autoreleased and will die after the end of the local scope.
If you need to have arr hang around inside the object. You should store it in a retained property:
#property (nonatomic,retain) NSMutableArray *arr;
... then use it with the self notation to ensure retention:
self.arr=[dataFactory fetchAllDrivers];
... then you need only release it in the class' dealloc method.
Having one object manage your data model is very good idea but it is not a "factory". Objective-c does not use factories like C++ and similar languages. Trying to think in those terms will lead to grief. The object should instead be thought of as a "controller" or "manager".