Memory not releasing when calling dealloc on NSFetchedResultsController - objective-c

I am trying to debug a memory leak within my code (2.5mb) and it seems to be pointing to _prepareResultsFromResultSet within NSFetchedResultsController. I have 2 viewControllers (A & B), viewController B is pushed on to the navigationController from viewController A.
In B I am performing an NSFetchRequest using NSFetchedResultsController as follows:
.h
#property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, retain) NSString *sMapSlug;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil andMapSlug: (NSString *)slug;
.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil andMapSlug: (NSString *)slug
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.sMapSlug = [NSString stringWithString:slug];
}
return self;
}
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil)
return _fetchedResultsController;
Singleton *singleton = [Singleton sharedSingleton];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DescriptionEntity" inManagedObjectContext:[singleton managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"map.sName" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"map.sSlug LIKE %#", self.sMapSlug];
[fetchRequest setFetchBatchSize:8];
[fetchRequest setPredicate:myPredicate];
// Finally check the results
NSError *error;
NSArray *fetchedObjects = [[singleton managedObjectContext] executeFetchRequest:fetchRequest error:&error];
for (DescriptionEntity *desc in fetchedObjects)
{
NSLog(#"Maps present in database: %#", desc.map.sName);
}
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[singleton managedObjectContext] sectionNameKeyPath:nil cacheName:#"Test1"];
self.fetchedResultsController = theFetchedResultsController;
self.fetchedResultsController.delegate = self;
//[self.fetchedResultsController performFetch:NULL];
[theFetchedResultsController release];
[fetchRequest release];
[sort release];
return _fetchedResultsController;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self.fetchedResultsController fetchRequest];
}
Everything is loaded in fine using the fetchRequest and I can access my data if need be, however I have taken this out for now so I know none of the data from the NSFetchedRequestController is being used. When I go back on the navigation stack the dealloc for controller B is called, and I perform the following:
- (void)dealloc {
self.fetchedResultsController.delegate = nil;
[_fetchedResultsController release];
[sMapSlug release];
[super dealloc];
}
When I take heap shots of this within Instruments I see the following data stay after the dealloc is called:
If I go back in to controller B again, another set of this memory is added and never removed. However, if I do this a 3rd time the amount does not increment. I am presuming that there is some sort of caching going on, can anyone tell me how to remove this data or how to correctly deallocate an NSFetchedResultsController.
If you need any more information feel free to ask.

Usually you should not worry about memory management in Core Data. Yes, under the hood a caching mechanism is involved.
Anyway, there are two different methods to "dismiss" the memory.
The first is refreshObject:mergeChanges: method. Passing to it NO, it allows to prune the object graph. In other words, it throws away any changed data that has not been saved to the store.
Override willTurnIntoFault and didTurnIntoFault to see it in action.
The other is to call reset on a managed context. Obviosly, this removes all the objects the context contains.
Hope that helps.

You should never ever call dealloc yourself, not even on your own objects. This is something the system does it self.

Related

NSUserDefaults array not saving properly

I have a problem i cant get my head around. I have an instance of NSUserDefaults and have stored some values in there. One of these values is an array that contains the players scores (its a game) to be used in another display. After the game is finished, I update the array with the new score and reassign it to the NSUserDefaults, but for some reason it doesn't seem to be behaving correctly and in debug when i try to print the count into he console, its always 0.
Here is the code in question:
// // SAMHighScoreInputViewController.m // Tappity // // Created by Sam on 29/05/13. // Copyright (c) 2013 Sam. All rights reserved. //
#import "SAMHighScoreInputViewController.h"
#import "SAMScoreObject.h"
#interface SAMHighScoreInputViewController ()
#end
#implementation SAMHighScoreInputViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self; }
- (void)viewDidLoad {
self.defaults = [NSUserDefaults standardUserDefaults];
self.nameScoreLabel.text = [NSString stringWithFormat:#"%# - %#", [self.defaults objectForKey:#"lastName"], [self.defaults objectForKey:#"lastScore"]];
self.nameTextField.text = [self.defaults objectForKey:#"lastName"];
[super viewDidLoad]; // Do any additional setup after loading the view. }
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated. }
- (IBAction)textFieldValueChanged:(UITextField *)sender {
self.nameScoreLabel.text = [NSString stringWithFormat:#"%# - %#", self.nameTextField.text, [self.defaults objectForKey:#"lastScore"]];
}
- (IBAction)submitButtonTapped:(UIButton *)sender {
SAMScoreObject *score = [[SAMScoreObject alloc] init];
score.playerName = self.nameTextField.text;
score.score = [self.defaults integerForKey:#"lastScore"];
NSArray *scores = [self.defaults objectForKey:#"scoresArray"];
NSMutableArray *updatedArray = [scores mutableCopy];
[updatedArray addObject:score];
NSArray *updatedArrayScores = [updatedArray copy];
[self.defaults setObject:updatedArrayScores forKey:#"scoresArray"];
[self.defaults setObject:[self.defaults objectForKey:#"scoresArray"] forKey:#"scoresArray"];
[self.defaults synchronize];
NSLog(#"%#, %#", [self.defaults objectForKey:#"lastScore"], [self.defaults objectForKey:#"lastName"]);
NSLog(#"%i", [[self.defaults objectForKey:#"scoresArray"] count]);
[self dismissViewControllerAnimated:YES completion:nil];
} #end
You need to serialize your SAMScoreObject into an NSData object of some form before you can store it in the NSUserDefaults store.
From the documentation:
A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
The fact that you're storing an NSArray object isn't good enough, because the objects in that array aren't one of the supported types.
Where is the scoresArray ever created? You should probably lazily create it when you attempt to update it. For example:
NSArray *scores = [self.defaults objectForKey:#"scoresArray"];
if(scores == nil)
{
scores = [NSArray array];
}
NSMutableArray *updatedArray = [scores mutableCopy];

Core Data Saving Attributes Of Entities

This question is about Core Data.
I created a Entity called TV with three attributes called name, price and size. I also created a subclass of NSMutableObject with TV.h and TV.m files.
I imported the TV.h to my DetailViewController.h which handles my sliders und UIElements I want to take the values of.
So I did a fetch request, and everything works fine, BUT:
Everytime I update the UISlider (valueDidChange:), Xcode creates a COPY of my entity and adds it to my TV-Object.
All I want Xcode is just to edit and save to the current entity, not to edit and save in a new entity.
Help is very appreciated!
Thank you in advance.
My Code:
DetailViewController.m
- (IBAction)collectSliderValue:(UISlider *)sender {
if (__managedObjectContext == nil) {
NSLog(#"Problem ...");
__managedObjectContext = [(MasterViewController *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"... solved!");
}
if (sender == sizeSlider) {
NSError *error = nil;
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TV" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
TV * currentTV = [[TV alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
currentTV.size = [[NSNumber alloc] initWithInt:(sender.value + 0.5f)];
currentTV.name = #"New TV!";
NSError *error11;
[__managedObjectContext save:&error11];
for (NSManagedObject *info in fetchedObjects)
{
NSLog(#"Name = %#", [info valueForKey:#"name"]);
NSLog(#"Size = %#", [info valueForKey:#"size"]);
NSLog(#"Price = %#", [info valueForKey:#"price"]);
}
[fetchRequest release];
}
//Editing begins ...
TV * currentTV = [[TV alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
Editing doesn't begin, you are creating a new object right there. Your view controller needs an instance variable to hold the current TV entity that you are modifying.
From the template project you have created, the variable detailItem contains the managed object that you are currently editing. You should specifically set this as a TV object, and refer to this instead of currentTV in your detailViewController code. You must remove all of the fetch request and managed object context code - this is not relevant in your detail view controller, it should be managed by the master view controller.
So, in DetailViewController.h:
#property (strong, nonatomic) id detailItem;
becomes
#property (strong, nonatomic) TV detailItem;
And in your collectSliderValue method, it should look something much more simple like this:
- (IBAction)collectSliderValue:(UISlider *)sender
{
if (sender == sizeSlider)
self.detailItem.size = [NSNumber numberWithFloat:sender.value];
}
The saving of the managed object context shouldn't occur until back in your detail view controller, this is taken care of in your application delegate.
In your master detail controller .m file you may also need to import the TV.h file so that it knows that TV is a NSManagedObject subclass. Also, cast to TV when you are setting the detail item:
self.detailViewController.detailItem = (TV*)selectedObject;

Locating a memory leak/source of over-release crach

Can anyone help me figure out where I should be releasing exerciseArray? I get a crash in dealloc and when I release just before calling sortedArray. This code is called many times.
//Set exerciseArray
review.exerciseArray = [[workout assignedExercises] allObjects];
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"viewWillAppear");
NSString *workoutName = [event.assignedWorkout valueForKey:#"name"];
self.title = workoutName ;
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"index"
ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[sortedArray release];
sortedArray= [exerciseArray sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"*****SORTEDARRAY****** %d",[sortedArray retainCount]);
for (Exercise *ex in sortedArray){
NSLog(#"%# %# %#",ex.name , ex.repCount, ex.weight);
}
NSLog(#"*********************");
[sortedArray retain];
[self.tableView reloadData];
}
- (void)dealloc {
[super dealloc];
[managedObjectContext release];
[event release];
}
First things first: You should move the [super dealloc] call to the end of your dealloc method. Handle your custom variables first, and then the last thing you do is push the dealloc up to the superclass to handle the rest of the cleanup.
Now, I'm concerned about the [sortedArray release] and [sortedArray retain] in there. What you should be doing, to save you the semantics of implementing retain/release on your sortedArray ivar, is declaring a property for sortedArray like so:
// this goes in your #interface
#property (nonatomic, retain) NSArray *sortedArray;
// this goes in your #implementation
#synthesize sortedArray;
Now you can set sortedArray easily without worrying about retaining or releasing:
// note the use of self, this is required
self.sortedArray = [exerciseArray sortedArrayUsingDescriptors:sortDescriptors];
You can remove the release and retain calls now, as this is handled automatically. Make sure to add a line to your dealloc method to clean up the variable, too..
self.sortedArray = nil;
..which will call release on your array for you.
EDIT: This also applies to exerciseArray, which was your actual question. Wherever there's a Cocoa class involved, you can simplify memory management using #property (retain) and #synthesize. For NSInteger and other primitive types, or when you don't want to hold an owning reference to the object, use #property (assign) instead.

UITableView returns null?

I have spent many hours looking into this and I still can't find a solution. Here's a chart on what the user goes through:
The user takes a picture or takes one from their camera roll
As soon as they take (or select) one, the captionView closes
As soon as the captionView closes, startUploads from a different view starts
If the user decides to open the view that startUploads is in, they can see what has been uploaded
Now that you know a little bit about the process, I have a few issues. When uploads finish I want them to fade out. I know how to do that, but for some reason when I call to, the self.theTableView returns null. I have a #property (nonatomic, retain)... in my .h and I have a #sythesize theTableView; in my .m.
In a different question, I found out that when doing the startUploads, my NSMutableArray needed to be initiated in here (so I thought that it would do the same with my UItableView). Here's what I have:
- (id)initWithNibNamed:(NSString *)nibName bundle:(NSBundle *)bundle {
self = [super initWithNibNamed:nibName bundle:bundle];
if (self) {
processList = [[NSMutableArray alloc] init];
self.theTableView = [[UITableView alloc] init];
NSLog(#"thetableview: %#", theTableView);
}
return self;
}
Just in case you want to see how startUploads is called, here's the code:
processViewController *testtest = [[processViewController alloc] initWithNibName:#"processView.xib" bundle:nil];
//testtest.processList = [[NSMutableArray alloc] initWithNig];
NSLog(#"Different:displayProcessVC BEFORE STARTING UPLOAD, processList = %#", testtest.processList);
NSLog(#"Different:displayProcessVC BEFORE STARTING UPLOAD, theTableView = %#", testtest.theTableView);
[testtest startUploads];
NSLog(#"Different:displayProcessVC AFTER STARTING UPLOAD, processList = %#", testtest.processList);
NSLog(#"Different:displayProcessVC AFTER STARTING UPLOAD, theTableView = %#", testtest.theTableView);
[testtest release];
However it still shows (null) in the console.
Please help!Coulton
EDIT 1:
Here's where it's being nil:
- (void)deleteOneRow: (NSString *)theString {
int theInt = [processList indexOfObject:theString];
[processList removeObjectAtIndex:theInt];
NSArray *deleteIndexPaths = [[NSArray alloc] initWithObjects: [NSIndexPath indexPathForRow:theInt inSection:0], nil];
NSLog(#"theTableView: %#", self.theTableView);
[self.theTableView beginUpdates];
[self.theTableView deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.theTableView endUpdates];
}
The designated initializer for UITableView is -initWithFrame:style: and you're leaking. Try this in -viewDidLoad instead.
self.theTableView = [[[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain] autorelease];

Get & Edit NSMutableArray from different class file

I am trying to access and change a array from a different class file. When using a NSLog, I get a result of (null). Below is my code:
RootViewController.h
NSMutableArray *listOfItems;
#property (nonatomic, retain) NSMutableArray *listOfItems;
RootViewController.m
#synthesize listOfItems;
listOfItems = [[NSMutableArray alloc] init];
[listOfItems addObject:#"One"];
[listOfItems addObject:#"Two"];
[listOfItems addObject:#"Three"];
SecondViewController.m
RootViewController *test = [[RootViewController alloc] init];
NSLog(#"Results: %#", test.listOfItems);
I get the following results in my console: Results: (null)
Thanks in advance,
Coulton
P.S. Obviously I have left out a bunch of code. I just tried to make it easier to read. If you need to see anything else, I would be more than happy to post more. Just ask
EDIT #1:
I am getting hundreds of NSLog Messages that look something like this:
*** __NSAutoreleaseNoPool(): Object 0x4e39020 of class __NSArrayI autoreleased with no pool in place - just leaking
And here's my init code:
- (id) init {
//NSLog(#"%#", theUserID);
// Set up database connection
NSString *myDB = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database.db"];
database = [[Sqlite alloc] init];
[database open:myDB];
//Initialize the array.
listOfItems = [[NSMutableArray alloc] init];
// Add to array to display in the tableView
NSArray *listOfItemsTwo = [database executeQuery:#"SELECT * FROM albums"];
for (NSDictionary *rowone in listOfItemsTwo) {
NSString *getName = [rowone valueForKey:#"name"];
if (getName != NULL) {
[listOfItems addObject:getName];
[getName release];
}
}
return self;
}
I guess you reversed RootViewController.m and RootViewController.h snippets right?
Are you sure that the
listOfItems = [[NSMutableArray alloc] init];
gets called? Maybe you can put a breakpoint there.
EDIT: Order of RootViewController.m and RootViewController.h has been fixed in the question. It's not clear from the question where the above line is in the code. That's a important piece of information.
EDIT2: Example of init method.
#implementation RootViewController
- (id) init
{
listOfItems = [[NSMutableArray alloc] init];
[listOfItems addObject:#"One"];
return self;
}
#end