Hi, as I'm a beginner to iPhone development, the memory management of Objective C is quite dazzling for me.
I've written a function which accesses a php page, returning JSON encoded data.
Via the JSON Touch library, I get a dictionary with the result
From there, I'm modifying the data so I can populate a UITableView with it
Before I write everything again (for the second time), here's my code
Code:
-(void)getContactsOnline {
//Define url to contact the db
NSURL *url = [NSURL URLWithString:@"<<the url>>"];
//String with returnvalue of the url
NSString *jsonReturn = [[NSString alloc] initWithContentsOfURL:url];
NSData *jsonData = [jsonReturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
NSError *error = nil;
//Make a dictionary of the received data
NSDictionary *dict = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error];
[jsonReturn release];
//Make an array with all the results in the dictionary with name "Contacts"
NSMutableArray *keys = [[NSMutableArray alloc] init];
if(dict)
keys = [dict objectForKey:@"Contacts"];
NSUInteger keysCount = [keys count]; //Count self.Keys elements
NSString *theFirstLetter;
//Finding all the first letters of the names of the employees: distinct.
//Loop through all the rows in keys to find the first characters of all contacts
NSUInteger index;
for(int i=0; i<keysCount; i++){
index = [self.sectionsInTable count];
//Grab the first character of name of contact i in array keys
theFirstLetter = [[NSString alloc] initWithFormat:@"%C", [[[keys objectAtIndex:i] objectForKey:@"name"] characterAtIndex:0]];
//If that character isn't in a new array with all the firstLetters, add it.
if([self.sectionsInTable indexOfObject:theFirstLetter inRange:NSMakeRange(0, index)] == NSNotFound){
[self.sectionsInTable addObject:theFirstLetter];
}
[theFirstLetter release];
}
[self.sectionsInTable sortUsingSelector:@selector(compare:)]; //Order the array alphabetically
/************************************************************************************\
Here the array will be populated with Contacts in an 2D array.
First loop is for the amount of distint first-character-letters. (ex: A,B,D,F,M,P,S) = 7
Second loop is to add the contact with a certain first-character-letter. (ex: Pablo, Pam, Peter, Paki) 4;
********************************************************************************/
NSMutableArray *inSection = [[NSMutableArray alloc] init];
NSString *newFirstLetter;
Contact *employee;// = [[Contact alloc] init];
NSUInteger sectionsInTableCount = [self.sectionsInTable count];
NSMutableArray *myArray;
//Loop through all the sections defined in the loop above
for(NSUInteger i = 0; i<sectionsInTableCount; i++){
//Make space (add array) for section i in array inSection
myArray = [[NSMutableArray alloc] init];
[inSection addObject:myArray]; //The sections (self.sectionsInTable count)
[myArray release], myArray = nil;
//Get the first character of the section name of section i
theFirstLetter = [[NSString alloc] initWithFormat:@"%C", [[self.sectionsInTable objectAtIndex:i] characterAtIndex:0]];
//Loop through the contacts in keys array
for(NSUInteger x=0; x<keysCount; x++){
//Grab the first character of the name of contact x
newFirstLetter = [[NSString alloc] initWithFormat:@"%C", [[[keys objectAtIndex:x] objectForKey:@"name"] characterAtIndex:0]];
//Check if the first character of the section name i is equal to the first character of the name of contact
if([theFirstLetter isEqualToString:newFirstLetter]){
//Add employee x with name and department to the array inSection
employee = [[Contact alloc] initWithName:[NSString stringWithFormat:@"%@", [[keys objectAtIndex:x] objectForKey:@"name"]] andWithDepartment:[NSString stringWithFormat:@"%@", [[keys objectAtIndex:x] objectForKey:@"department"]]];
[[inSection objectAtIndex:i] addObject:employee];
[employee release];
}
[newFirstLetter release];
}
[theFirstLetter release];
}
// [keys release];
id objectInstance;
id objectInstance2;
NSMutableDictionary *mutableDictionary = [[NSMutableDictionary alloc] init];
NSString *letterKey;
NSMutableArray *contactsArray;
//Loop through all the arrays in the array inSection
for (objectInstance in inSection) {
//Grab the first character of the first contact in array nr objectInstance-nr for the sectionName
letterKey = [[NSString alloc] initWithFormat:@"%C", [[[objectInstance objectAtIndex:0] name] characterAtIndex:0]];
//Loop all the contacts of the array objectInstance in array inSection
contactsArray = [[NSMutableArray alloc] init];
for(objectInstance2 in objectInstance){
//Add all the contacts to a new array
[contactsArray addObject:objectInstance2];
}
//Once all contacts are added, add the new array to a new dictionary, with the letterKey as key
[mutableDictionary setObject:contactsArray forKey:letterKey];
[contactsArray release];
[letterKey release];
}
//Copy the dictionary into the propery allNames
self.allNames = [mutableDictionary mutableDeepCopy];
NSLog(@"MutableDic Count: %d",[mutableDictionary retainCount]); //1
[mutableDictionary release];
NSLog(@"MutableDic Count: %d",[mutableDictionary retainCount]); //1
NSLog(@"keys Count: %d",[keys retainCount]); //2
NSLog(@"jsonData Count: %d",[jsonData retainCount]); //1
NSLog(@"error Count: %d",[error retainCount]); //0
NSLog(@"dict Count: %d",[dict retainCount]); //1
NSLog(@"theFirstletter Count: %d",[theFirstLetter retainCount]); //2
NSLog(@"inSection Count: %d",[inSection retainCount]); //1
NSLog(@"employee Count: %d",[employee retainCount]); //2
NSLog(@"LetterKey Count: %d",[letterKey retainCount]); //2
//Correctly released: //Crash on calling retainCount method
/*
- newFirstLetter
- jsonReturn
- contactsArray
*/
}
I removed all (except last) debugging NSLog's for readability.
So, at the end, I wrote all retainCounts of all allocated variables.
With the returned value commented out behind it.
But why aren't the returned values zero, or just crashing my app? (which I think should happen)
What's going on there? oO
The analyzer even warns me with: "Reference-counted object is used after it is released"
- even weirder, when i set that last NSLog-mutableDictionary-retainCount below the last NSLog in the function, it seems to behave as expected (crashing the app)
I'm quite consistent with releasing allocated objects (i think)
so another weird part. the keys array.
Allocated as follow:
After that, just using the contents, never modifying it again.
Yet, [keys release] isn't allowed.
App either crashes, or says: object is not owned by you.
I know :| a lot of questions with a bunch of code.
After consulting tutorials, books and google, it still dazzles me.
Help highly appreciated!
Grtz,
Me
Pfff, second time I wrote this topic... first time got logged out in the background OMG!
After a variable is released for the last time, the retain count is not actually set to zero because the memory is simply put in a pool of available memory that is available for new allocs. It may be allocated to some other purpose immediately or it may just sit there for a while looking exactly the way it did before it was released. Either way, you should not try to access it, even to look at its retain count. Actually there is very little practical use for the retain count property to you as the application programmer. There is too much going on behind the scenes to make that value reliable to you.
This is wrong because keys is first set to some newly alloced and inited memory, then it is reassigned to an auto-released object, leaving that first block of memory orphaned as a leak.
Releasing keys after that does no good because keys no longer refers to that first alloced block of memory. And it is also wrong because now keys refers to an auto-released object which should not be explicitly released.
For the retainCount,
I come from a background with knowledge in C++.
When 'delete object' is done, even 1 line after the delete statement, there is no way to access that previously deleted object.... well, there is. but didn't encounter it :P
Given the fact that allocated space is still reachable makes sense for these lines of code:
where the last line isn't valid anymore after 3 lines :P
Yea, i figured retainCount is of little use to developers, but it was my only way to see if I correctly released everything (as testing purpose)
btw: I can safely assume the analyzer sees all leaking objects? If this question is to Xcode 4 oriented, you can ignore it :P, I don't know if it's available in Xcode 3
Then again, Thank you for your help!
Made things much clearer to me
ow, one final thing, I run analyzer again, and it seems
is a Potential leak of an objoect and stored into 'inSection'
Does that honestly have to do with ' = [[NSMutableArray alloc] init]'
or with the numerous:
Hi, as I'm a beginner to iPhone development, the memory management of Objective C is quite dazzling for me.
I've written a function which accesses a php page, returning JSON encoded data.
Via the JSON Touch library, I get a dictionary with the result
From there, I'm modifying the data so I can populate a UITableView with it
Before I write everything again (for the second time), here's my code
I removed all (except last) debugging NSLog's for readability.
So, at the end, I wrote all retainCounts of all allocated variables.
With the returned value commented out behind it.
But why aren't the returned values zero, or just crashing my app? (which I think should happen)
What's going on there? oO
The analyzer even warns me with: "Reference-counted object is used after it is released"
- even weirder, when i set that last NSLog-mutableDictionary-retainCount below the last NSLog in the function, it seems to behave as expected (crashing the app)
I'm quite consistent with releasing allocated objects (i think)
so another weird part. the keys array.
Allocated as follow:
After that, just using the contents, never modifying it again.
Yet, [keys release] isn't allowed.
App either crashes, or says: object is not owned by you.
I know :| a lot of questions with a bunch of code.
After consulting tutorials, books and google, it still dazzles me.
Help highly appreciated!
Grtz,
Me
Pfff, second time I wrote this topic... first time got logged out in the background OMG!
I haven't read your code - I've got my hands full this morning.
In general, though:
Looking at the retainCount for your objects is not very useful, for a number of reasons.
First and foremost, there's autorelease. If you send an autorelease message to an object, that object gets added to an "autorelease pool". An autorelease pool is a list of objects that will be sent a release message the next time your code returns and the app visits the event loop. The act of sending releases to all the objects in the autorelease pool is known as "draining" the autorelease pool. You can send an autorelease message to an object more than once. If you do, it gets sent multiple release messages when the release pool is drained.
Many system methods return autoreleased objects. It's an excellent way to return objects, because the caller can just use the object and forget about it, and it gets cleaned up later. stringWithFormat is an example of this:
Code:
int points = 10;
NSString* aString = [NSString stringWithFormat: @"You have %d points", points];
NSLog(@"aString retainCount = %d", [aString retainCount])
//Displays 1 because stringWithFormat returns an autoreleased object.
//aString will go away unless you retain it.
[aString retain];
NSLog(@"aString retainCount = %d", [aString retainCount])
//Displays 2 after another retain
[aString autorelease];
NSLog(@"aString retainCount = %d", [aString retainCount])
//Still displays 2, but object will be released soon.
The system will also sometimes retain pointers to your objects if it needs them again. Sometimes temporarily, and sometimes long-term.
There are other games the system frameworks play with your objects that cause the retainCount to be different than what you expect. (For example, sending a copy message to an immutable object might return a new copy of an object, but it is just as likely to simply increment the retain count and return the same object. Because the object is immutable (can't be changed) you don't need to care which it does, but because you don't know which, you don't know what the retain count of the original object will be.
The result of all this is that you really can't use retainCount to tell what's going on with your objects.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.