Weird memory leak with core data - iphone

Instruments detect a memory leak:
(Leaked Object= "__NSCFString")
This is my code:
-(NSArray*)loadAllPages{
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Page" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:#"date" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
NSError *error = nil;
myArray = [[NSArray alloc ]initWithArray:[[context executeFetchRequest:fetchRequest error:&error]autorelease]];
[fetchRequest release];
if (myArray == nil)
NSLog(#"%#",error);
return myArray;
}
The line where leak is pointed at is initialization of myArray.
If I do not return myArray, this leak doesn't occur, so this is strange for me.

In this line
myArray = [[NSArray alloc ]initWithArray:[[context executeFetchRequest:fetchRequest error:&error]autorelease]];
the square brackets are slightly wrong.
The autorelease is sent to the result of [context executeFetchRequest:...], not to the allocated array.
[context executeFetchRequest:...] returns an autoreleased array. Sending autorelease to that array is an error. And myArray is allocated, but not released in your code.
The Xcode static analyzer also reports this as an error:
I assume that you meant
myArray = [[[NSArray alloc] initWithArray:[context executeFetchRequest:fetchRequest error:&error]] autorelease];
which would result in an autoreleased array myArray.

If you are not using ARC.
Change return myArray; with return [myArray autorelease];
You are returning an allocated object from your method, the ownership of that object is still with the method, in such scenarios you should return an auroreleased object and retain it on the receiving side. Then the ownership of that object will be with the calling method.

Related

Filter NSFetchedResultsController to remove objects with same name

I have these objects that are unique except for two columns once of which I am using as the display in the UITableView. The UITableView will often show duplicates due to this. I need to filter out these duplicates somehow.
Setting distinctResult on the FetchResult won't work in this case since that limits the functionality of the NSFetchedResultsController and it requires that the REsultType be a NSDictionary instead of a subclass of managed object.
Doesn't anyone have any idea how I can filter these duplicates out using a predicate?? Remember that every field on these objects is unique except for two of them.
-(NSFetchedResultsController *) fetchGroupedObjects:(NSString *)entityDescription
sortField:(NSString *)sortField
withPredicate:(NSPredicate *)predicate {
BPRAppDelegate *delegate = (BPRAppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = delegate.managedObjectContext;
//NSError *error;
//Fetch the data....
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:entityDescription inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *groupDescription = [[NSSortDescriptor alloc]
initWithKey:GROUP_NAME ascending:YES];
//Sort by Category Name
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:sortField ascending:YES];
NSMutableArray *sorts = [[[NSMutableArray alloc] init] autorelease];
[sorts addObject:sortDescriptor];
[sorts addObject:groupDescription];
[fetchRequest setSortDescriptors:sorts];
//[fetchRequest setResultType:NSDictionaryResultType];
//[fetchRequest setPropertiesToGroupBy:[entity.propertiesByName valueForKey:CONTRACTOR_NAME];
if (predicate != nil)
[fetchRequest setPredicate:predicate];
//NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
//NSFetchResultsController
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *fetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context sectionNameKeyPath:GROUP_NAME
cacheName:nil]; //Don't use a cache
[fetchRequest release];
[sortDescriptor release];
[groupDescription release];
return fetchedResultsController; //You can't autorelease this thing... the requestor must do that.
}
While it might be easier to set up an optional to-one relationship back to itself (say, "child") so that you can then fetch with a NSPredicate where "child == nil", you can certainly post-filter easily enough in pure objective C using [array enumerateObjectsUsingBlock:] or equivalent, and only add unique objects per your criteria to the resultant array.

How to extract specific attribute from core date store

I'm executing a fetch request with sort descriptor to populate a mutable array as follows:
NSFetchRequest *requestA = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YearPhoto" inManagedObjectContext:managedObjectContext];
[requestA setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[requestA setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
NSMutableArray *mutableFetchResultsA = [[managedObjectContext executeFetchRequest:requestA error:&error] mutableCopy];
if (mutableFetchResultsA == nil) {
}
[self setImageArray:mutableFetchResultsA];
[mutableFetchResultsA release];
[requestA release];
I need to extract the attribute of YearPhoto, friendsPhoto (UIImage ~60KB stored in core data store) to setImageArray for use here in an animation within a UIImageView.....
self.theImageView.animationImages = [NSMutableArray initWithArray:imageArray];
I have also tried:
self.theImageView.animationImages = [NSArray initWithObjects:yearPhoto.friendsPhoto, nil];
I can't work it out. Checked docs but they are not very clear on this specific problem
Anyone see the error in my ways?
Assuming that "Year Photo" is being returned as an Entity you should take the object out of the mutableFetchResultsA and then lookup the friendsPhoto from that object. I'm also not sure what you datatypes are but I'm assuming friendsPhoto is of same type as ImageArray. It would be something like this:
YearPhoto* photo = [mutableFetchResultsA objectAtIndex:0];
[self setImageArray:photo.friendsPhoto];

Memory leak when retrieving data from database

Hey, I've created a custom retrieval method for database access:
+(NSArray*) recordsForTable:(NSString *)table predicate:(NSPredicate *)prd{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:table inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:prd];
NSArray *records = [managedObjectContext executeFetchRequest:fetchRequest error:nil];
[fetchRequest release];
return records;
}
i then use the above method in this method:
-(NSArray *)tableViewControllerData{
NSNumber *savedBool = [[NSNumber alloc] initWithBool:YES];
NSString *onlyGetSavedVisitObjects = [NSString stringWithFormat:#"bolSaved=%#", savedBool];
[savedBool release];
NSMutableArray *data = [[[CoreDataAccess recordsForTable:#"LPVisit" stringPredicate:onlyGetSavedVisitObjects] mutableCopy] autorelease];
NSSortDescriptor *dateDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"dteVisitDate" ascending:NO];
NSArray *descriptors = [NSArray arrayWithObjects:dateDescriptor, nil];
[data sortedArrayUsingDescriptors:descriptors];
return data;
}
The trouble am having is that when the user makes changes to the LPVisit table and recall this method to show those changes it crashes the application.
[EDIT]
The exception it produces is:
-[__NSArrayM objectID]: unrecognized selector sent to instance 0x4dac1f0
I believe the error is at line:
NSMutableArray *data = [[[CoreDataAccess recordsForTable:#"LPVisit" stringPredicate:onlyGetSavedVisitObjects] mutableCopy] autorelease];
If I remove the autorelease, I get a memory leak but the application doesn't crash.
Does anyone have any insights, thanks in advance
Is it possible that the mutable copying is throwing the exception because *records is nil? This might happen if #"bolSaved=%#" is a typo and should be #"boolSaved=%#".

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

How to properly configure NSFetchedResultsController

I am placing an NSFetchedResultsController into my code so I get that nice automatic sectioning of my table view data.
So I am running a test to make sure everything works properly. I have a single Book entity in my persistent store. I will first perform the fetch the old way, then I will try to use NSFetchedResultsController. The difference between the 2 blocks of code is just 2 lines.
Code without NSFetchedResultsController:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:kBookEntityName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
//The following 2 lines will be replaced by the NSFetchedResultsController
NSMutableArray *mutableFetchResults = [[[self.managedObjectContext executeFetchRequest:request error:nil] mutableCopy] autorelease];
Book *result = (Book*)[mutableFetchResults objectAtIndex:0];
NSString* title = [result valueForKey:#"title"];
NSString* priority = [result valueForKeyPath:#"priority.name"];
[request release];
Now I substitute in the lines for the NSFetchedResultsController:
NSFetchedResultsController* fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"title" cacheName:#"BookList"];
Book *result = (Book*)[fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
Seems pretty cut and dry. The first code block properly fetches the single Book entity. The code with the NSFetchedResultsController, however, does not. Instead it returns nil.
My question is: Am I properly configuring the NSFetchedResultsController in this example?
(note, the fetchedObjects property of the NSFetchedResultsController is also nil)
I think you still need to tell the NSFetchedResultsController to actually perform the fetch:
NSError *error;
BOOL success = [controller performFetch:&error];
(taken from the example in the NSFetchedResultsController reference)
one other thing that seems odd: do you really want to use "title" as the sectionNameKeyPath? won't that basically create a separate section for each book?
You need to initialize your NSFetchedResultsController only once, as follows.The code assumes
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
are declared in the header of your controller, and that managedObjectContext is already properly initialized.
- (void)viewDidLoad {
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
NSLog(#"%d objects fetched", [[fetchedResultsController fetchedObjects] count]);
}
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:kBookEntityName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
/* Optional settings
[request setResultType:NSManagedObjectResultType];
[request setIncludesPropertyValues:YES];
[request setIncludesPendingChanges:NO];
[request setReturnsObjectsAsFaults:NO];
*/
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"title" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];;
// 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:request managedObjectContext:managedObjectContext sectionNameKeyPath:#"title" cacheName:#"BookList"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[request release];
return fetchedResultsController;
}