Memory leak when refreshing table view with retain iOS - iphone

Most of the memoryleaks I solved myself, but this one is quite tough imo. The following happens. I need to load information from facebook in a table view, this table view has an refresh function. All the rows in this tablview are loaded from an array, this arrays consists of data objects as they need to be sorted. My code looks like this (I have cut out the irrelevant parts).
This parts runs through the results from facebook and places it in an array
- (void)request:(FBRequest*)request didLoad:(id)result
{
if ([result isKindOfClass:[NSDictionary class]]) {
//Setting single result into result dictionary
NSArray *resultArray = [result allObjects];
result = [resultArray objectAtIndex:0];
for(int i=0; i<13; i++){
//Set all retrieved data in containerArray
Post *newPost = [[[Post alloc] init] autorelease];
newPost.created_time = created_time1;
newPost.message = message1;
newPost.picture = picture1;
newPost.fbSource = fbSource1;
[containerArray insertObject:newPost atIndex:i];
//Reload the table in the tableView
[self.tableView reloadData];
}
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"created_time"
ascending:NO] autorelease ];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
}
}
So far this works and gives no memory leaks. But as soon as the refresh function gets called. This function will run again. And then creates the memory leak, I think probably due the [sortedArray retain] function. But without this function the array doesn't load and I get a EXC_BAD_ACCESS. If I release sortedArray, I also get the EXC_BAD_ACCESS since the sortedArray is gone and can't be called.
Someone knows how to fix this? Thnx!

Your diagnosis is right. If you assign a value to sortedArray a second time the way you are doing, the previous object is leaked.
The solution is calling release before doing the assignment:
[sortedArray release];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
A more elegant solution would be declaring sortedArray as a retain property:
#property (nonatomic, retain) NSArray* sortedArray;
so that you can replace the three lines above by:
self.sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
and this will handle both releasing and retaining properly.

sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
This line runs for first time and this is OK. But run 2nd time and you are pointing to a new array, leaking the previous one. So there is two solution.
First, release it before this line like this:
[sortedArray release];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
Or make sortedArray a retained property in your class.
#property (nonatomic, retain) NSArray *sortedArray;
self.sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];

Related

Memory Leak in the below simple code

I have a piece of code & I want to understand the memory leak possibility in this. I have gone through the Apple documentation here!
The code here process a array from parameter & stores the dictionary into a temporary array.
+ (void)setLinkedProfiles:(NSArray *)profileData {
NSMutableArray *returnArray = [[NSMutableArray alloc]init];
if([profileData count] > 0) {
for(NSDictionary *dict in profileData) {
NSDictionary *tempDict = #{
#"verifiedEmail" : ([[dict objectForKey:#"verifiedEmail"] isKindOfClass:[NSNull class]]) ? #"": [dict objectForKey:#"verifiedEmail"],
#"identifier" : [dict objectForKey:#"identifier"],
};
[returnArray addObject:tempDict];
}
}
[SharedApp sharedUserData].linkedProfiles = returnArray;
}
I suspect that the line of code SharedApp sharedUserData].linkedProfiles = returnArray might create a memory leak.
I want to understand why & in which scenario?
Can some one help me in understanding this ?
Thank you for reading & understanding my problem.
To clear things up:
Your line
NSMutableArray *returnArray = [[NSMutableArray alloc]init];
gives you ownership of that array. You are responsible to release it when you're done with it.
So adding
[returnArray release];
as the last line would work. You could use autorelease instead, or even work with an autoreleased array from the beginning, i.e.
NSMutableArray *returnArray = [NSMutableArray array];
linkedProfiles should be a strong reference (i.e. strong, or copy).
Although I strongly suggest to switch to ARC, understanding of the underlying memory management might come handy.

Declared and synthesized NSArray works in all methods but one

I have an array of NSMutableDictionaries which has been sorted.
This array has been declared and synthesized so that its reachable anywhere in the code. However, its not.
When I try to read it out in cellForRowAtIndexPath, I get 0x5852400 does not appear to point to a valid object in debugger by using the po command, and I get the EXC_BAD_ACCESS error when using NSLog.
Code:
- (void)request:(FBRequest *)request didLoad:(NSArray*)result
{
int counter;
friendsArray = [[NSMutableArray alloc] init];
for (NSDictionary *d in [result objectForKey:#"data"])
{
[friendsArray addObject:d];
counter++;
}
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"first_name" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
newfriendsArray = [[NSArray alloc] init];
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"The new array which works and has been sorted: %#", newfriendsArray);
[[self tableView] reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Array still working here: %#", newfriendsArray);
return [newfriendsArray count];
}
Doing the same NSlog like the ones above in cellForRowAtIndexPath will cause the simulator to crash.
It's a memory management error. In this line:
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
sortedArrayUsingDescriptors: returns an autoreleased object that you need to retain if you want to use it for longer than the current method. The line above:
newfriendsArray = [[NSArray alloc] init];
has no effect other than introduce a memory leak to your code. It seems you have not quite understood when you need to create new objects and when other methods do this for you. In addition, you should really use properties to set your ivars (self.newFriendsArray = ...;) to avoid these simple memory management errors.

iPhone - accessing an NSArray in a ViewController from the AppDelegate?

I want to be able to access an array of objects in my iPhone application. The array of objects is populated in the appDelegate of my application and I want to be able to access the array in one of my View Controllers.
I currently set up the array in my appDelegate.h file as follows:
NSArray *listObjArray;
#property (nonatomic, retain) NSArray *listObjArray;
I then populate it with some Strings like this in the AppDelegate:
listObjArray = [NSArray arrayWithObjects:#"Hello", #"How", #"are", nil];
NSLog(#"Array size = %i", [listObjArray count]);
It is synthesized and also released in dealloc. The NSLog returns the correct count here.
In my ViewController class I import the appDelegate like this:
#import "MyaAppDelegate.h"
I then access my appDelegate and the NSArray like this and try to Log the count in my View Controller:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication]
delegate];
NSLog(#"Before array set");
NSArray *newArray = [appDelegate listObjArray];
NSLog(#"After array set");
NSLog(#"array count = %i", [newArray count]);
NSLog(#"After array count");
The logging here gets to "After array set" and then I get "EXC_BAD_ACCESS" on the line where I try to print the count from the array in the View Controller.
The printing of the count works fine from the appDelegate and setting the newArray as the array from the delegate appears to work yet I cant do anything with it then.
Can anyone see what I am doing wrong?
I think your array declaration should be:
NSArray *newArray = [[NSArray alloc] initWithArray:appDelegate.listObjArray]
Be sure to release it after you are done! Though I'm not sure why you want to declare the new array, you could just do:
NSLog(#"array count = %i", [appDelegate.lstObjArray count]);
Hope this helps!
-Karoly
You have a memory issue: listObjArray = [NSArray arrayWithObjects:#"Hello", #"How", #"are", nil]; sets the instance variable directly. Shortly after this line the array gets released again, which results in you accessing a bad memory location in NSArray *newArray = [appDelegate listObjArray];, since it has been freed.
Use self.listObjArray = ... instead when populating the array. This will properly retain the object for you.
Please use the getter if it is sythesized. Since you are not using the getter, it is giving you bad memory access.
Also you should use retain or copy if you want to retain it or copy it. Else both newArray and listObjectArray will point to same memory location causing bad behavior.
NSArray *newArray = [[appDelegate getListObjArray] retain];
Try this
self.listObjArray = [NSArray arrayWithObjects:#"Hello", #"How", #"are", nil];
Allocation should be made on the getter.

Releasing NSArray containing NSDictionary objects

I am having difficulty getting my head around memory management in the following segment of code on iPhone SDK 3.1.
// Create array to hold each PersonClass object created below
NSMutableArray *arrayToReturn = [[[NSMutableArray alloc] init] autorelease];
NSArray *arrayOfDictionaries = [self generateDictionaryOfPeople];
[arrayOfDictionaries retain];
for (NSDictionary *dictionary in arrayOfDictionaries) {
PersonClass *aPerson = [[PersonClass alloc] init];
for (NSString *key in [dictionary keyEnumerator]) {
if ([key isEqualToString:[[NSString alloc] initWithString: #"FIRST_NAME"]])
aPerson.firstName = [dictionary objectForKey:key];
else if ([key isEqualToString:[[NSString alloc] initWithString: #"LAST_NAME"]])
aPerson.lastName = [dictionary objectForKey:key];
}
// Add the PersonClass object to the arrayToReturn array
[arrayToReturn addObject: aPerson];
// Release the PersonClass object
[aPerson release];
}
return arrayToReturn;
The [self generateDictionaryOfPeople] method returns an array of NSDictionary objects. Each NSDictionary object has two keys "FIRST_NAME" and "LAST_NAME" with a person's first name and last name as the respective data. The code is looping through each dictionary object in the arrayOfDictionaries array and assigning the dictionary data to the relevant property of an aPerson (PersonClass) object. This object is then added to an array which is returned from this method.
When running instruments I am getting a leak for the dictionary objects contained in the arrayOfDictionaries array. The code within the [self generateDictionaryOfPeople] method is calling [dictionaryObject release] on each NSDictionary object as it is created and added to the array, which makes the retain count on the object 1 (as adding the object to the array would make the retain count 2, but then my release message decrements it back to 1).
I assume this leak is because I am never releasing the arrayOfDictionaries array, and thus the NSDictionary objects within the array are never released. If I attempt to release the array at the end of the above segment of code I get a "message sent to deallocated instance" error. I understand why this is occurring, because I am assigning the aPerson object data within a dictionary item (that I am subsequently releasing) but I don't know where else I can release the arrayOfDictionaries array. Any help would be greatly appreciated.
Thanks!
EDIT: Below is the implementation for [self generateDictionaryOfPeople]
- (NSArray *)generateDictionaryOfPeople {
NSMutableArray *arrayFromDatabase = [[[NSMutableArray alloc] init] autorelease];
// ** Query the database for data **
while ( there are rows being returned from the database ) {
// Declare an NSMutableDictionary object
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
// Loop through each column for that row
for ( while there are columns for this row ) {
columnTitle = title_of_column_from_database
columnData = data_in_that_column_from_database
// Add to the dictionary object
[dictionary setObject:columnData forKey:columnTitle];
// Release objects
[columnName release];
[columnTitle release];
}
// Add the NSMutableDictionary object to the array
[arrayFromDatabase addObject:dictionary];
// Release objects
[dictionary release];
}
// Return the array
return arrayFromDatabase;
}
Here,
if ([key isEqualToString:[[NSString alloc] initWithString: #"FIRST_NAME"]])
aPerson.firstName = [dictionary objectForKey:key];
else if ([key isEqualToString:[[NSString alloc] initWithString: #"LAST_NAME"]])
aPerson.lastName = [dictionary objectForKey:key];
Replace them with
if ([key isEqualToString:#"FIRST_NAME"])
aPerson.firstName = [dictionary objectForKey:key];
else if ([key isEqualToString:#"LAST_NAME"])
aPerson.lastName = [dictionary objectForKey:key];
The problem of the leak is you're creating 1 ~ 2 NSString-s per loop without -release-ing them. If you need constant NSString-s, just directly use them.
I am still getting the original leak due to not releasing the arrayOfDictionaries array.
That means you forgot to autorelease it in generateDictionaryOfPeople.
You need to review the memory management rules.
You are not releasing arrayFromDatabase. (The simplest way to avoid this kind of mistake is to use factories and autorelease as early as possible rather than defer releases manually. In this case, use [NSMutableDictionary dictionary] instead of [[NSMutableDictionary alloc] init].)

Objective c, Memory Leak, reading from sqlite and assigning values to a NSDictionary and NSAarray

I have a list of shops in a ListController file.
I've setted up a sqlite db, in which i've stored 60 shops.
On the top of the list i have a search bar.
I've made a class called DataController, that is responsible to load and store db datas.
#interface DataController : NSObject {
sqlite3 *database;
NSArray *shops;
NSDictionary* dictionaryOfShops;
}
#property (nonatomic, retain) NSDictionary *dictionaryOfShops;
#property (nonatomic, retain) NSArray* shops;
-(void)initializeShops;
initializeShops method loads data from the db, and stores results into the 2 props in this way:
-(void)initializeShops{
[dictionaryOfShops release];
[shops release];
NSMutableDictionary *dictionary = [[[NSMutableDictionary alloc] init] autorelease];
if (sqlite3_open(....))
NSString *query = ....
if (sqlite3_prepare_v2(database, [query UTF8String],-1, &statement, nil) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW) {
int rId = sqlite3_column_int(statement, 0);
char *rName = (char *)sqlite3_column_text(statement, 1);
Shop* s = [[Shop alloc] init];
s.ID = rId;
if(sName != nil) s.Name = [NSString stringWithUTF8String:rName];
NSString *shopID = [[NSString alloc] initWithFormat:#"%d",s.ID];
[dictionary setObject:s forKey:shopID];
[shopID release];
[s release];
}
sqlite3_finalize(statement);
}
[query release];
dictionaryOfShops = [[NSDictionary alloc] initWithDictionary:dictionary];
shops = [[NSArray alloc] initWithArray:[dictionary allValues]];
dictionary = nil;
[dictionary release];
//Sorting
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES];
NSArray *sortedList =[self.shops sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
self.shops = sortedList;
[sort release];
}
The problem is that when user enters some text into the search
bar, I change the value of the query (adding LIKE....) and then call the initializeShops method again. This second time makes
so many leaks, (related to the Shop class properties) and
leaks also a NSDictionary and a NSArray.
Before posting this to you I've tried different solutions, but
at least this doesn't leaks anything the first time I call
initilizeShops.
I accept any suggestion, since I'm really stuck
on it.
MORE:
The really strange thing is memory management of my var dictionary and the 2 props shops and dictionaryOfShops. With this code
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
//add data to dictionary
dictionaryOfShops = [[NSDictionary alloc] initWithDictionary:dictionary];
shops = [[NSArray alloc] initWithArray:[dictionary allValues]];
[dictionary release]
Considering that dictionaryOfShops and shops are two properties (nonatomic,retain) synthesized, how can I change value to them without leaks?
The very first time I pass through this method nothing gets leaked, from the second time it starts to leak so many objects (the contents of the collections).
The first question is Why not just use Core Data? It is very likely going to be faster, will require less code, and will be significantly easier to maintain over time. To be blunt; SQLite is deceptively hard. Easy to get started, exceptionally difficult to get right.
In any case, the memory management of dictionary is wrong. It only isn't crashing because you swapped the order of the nil assignment and release as kennyTM suggested. I would suggest not creating an autoreleased dictionary.
Otherwise, the code as written seems pretty leakless at first glance. So:
Can you provide some more code?
Anything interesting memory wise
going on elsewhere?
Are you using threading at all (or
NSOperationQueue)?
Have you run under the Leaks
instrument and retrieved the
backtraces of allocation of the
specific objects being leaked?
dictionary = nil;
[dictionary release];
Please swap these 2 statements. In this form it means [nil release] which is a no-op.
Ok, I've found the error.
In my class Shop, i realize i didn't implement the method
-(void)dealloc
So when I release the old dictionary (to prepare for a new assignment), all the fields inside of it didn't get released.