I have a code which shows leaks in Instruments. It shows leaks where I initialize the arrayDetPerformance with the contents of arrayDetail
If I release my arrayDetail then my app crashes.
What could be wrong?
Here is the code:
NSDictionary *finalResult = [extractUsers JSONValue];
// NSLog(#" Stamp-16 : %#",[NSDate date]);
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 0
arrayDetail = [[finalResult objectForKey:#"Detail"]
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 2
// NSLog(#"Data is : %#",array1);
// NSLog(#" Stamp-17 : %#",[NSDate date]);
//NSLog(#"Final Value is : %#",[[allUsers objectAtIndex:0] valueForKey:#"password"]);
//[self setUserData:allUsers];
//[tblView reloadData];
[responseString release];
[request release];
}
//sleep(0.3);
//[inProgressIndicator stopAnimating];
[fileContents release];
//Release all the allocated data
[json release];
//label.text = #"Finish";
// NSLog(#" Stamp-19 : %#",[NSDate date]);
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
//NSLog(#"Array2 : %d",[array2 retainCount]);
arrayDetPerformance = [[NSMutableArray alloc] initWithArray:arrayDetail];
chartPoints= [arrayDetPerformance valueForKey:#"Points"];
NSLog(#"Chart Points: %#",chartPoints);
[def setObject:chartPoints forKey:#"yarray"];
[def setObject:#"YES" forKey:#"flagThumb"];
//array1 = [[NSMutableArray alloc] initWithObjects:#"New",#"Table",#"View",nil];
//[self.Dettable reloadData];
//sNSFileManager *fileManager = [NSFileManager defaultManager];
//[array2 release];
NSLog(#"ArrayDEtPerfomance : %d",[arrayDetPerformance retainCount]);
NSLog(#"array2 : %d",[arrayDetail retainCount]);
if([chartPoints count]>0)
{
PlotItem *plotItem = [[PlotGallery sharedPlotGallery] objectAtIndex:0];
[plotItem imageHive:Fund];
}
//[arrayDetail release];
}
Memory leak is shown on the line
arrayDetPerformance = [[NSMutableArray alloc] initWithArray:arrayDetail];
Also I am confused on why the retain count directly goes from 0 to 2 in the below code:
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 0
arrayDetail = [[finalResult objectForKey:#"Detail"]
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 2
What could be wrong?
It shows a leak because you allocate arrayDetPerformance and then not release it. Simple as that. At least that's what we can tell from the code you are showing us.
As for the rest, don't use retainCount to debug memory problems, ever! You have to understand the simple memory management rules and follow them, nothing else. Since you don't know what Apple's underlying code does, you cannot rely on the retain count of an object.
As to your question relating to this code:
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 0
arrayDetail = [[finalResult objectForKey:#"Detail"]
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 2
You are assigning a whole other object to arrayDetail so it's completely meaningless to compare any properties of arrayDetail before and after the assignment. The retain count could be all over the place and it would not tell you anything.
I get the impression that you don't really know what you are doing here. You should read the memory management rules again and again until you understand them thoroughly.
retainCount won't help you debug your problem (in fact it will almost never be relevant to debugging, so best forget it's even there).
Don't release arrayDetail, as you don't own it. The problem is with arrayDetPerformance. You're allocing an object on that line, and it's not being released anywhere. Now, you may be doing that elsewhere in your code, but if you aren't, send it a release when you've finished using it.
Edit
If you're deallocating arrayDetPerformance in your dealloc method, I'm assuming it's an instance variable? In this case, you can't assume that it doesn't already point at an object, so you should send it a release before assigning it to the new object.
Alternatively, if it is configured as a property, just use self.arrayDetPerformance = ... which will take care of the memory management for you.
Do not call retainCount
retainCount is useless. The absolute retain count of an object is an implementation detail. The rule is simple; if you cause something to be retained, you must cause it to be released when you are done with it. End of story.
The memory management documentation discusses this fully.
First, retainCount can never return zero. The only time you'll get a zero is if you happened to message nil. This:
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 0
arrayDetail = [[finalResult objectForKey:#"Detail"]
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 2
You are causing arrayDetail to point to a different object on that second line. Thus, no relation between the retain count before/after that line.
When leaks tells you a leak is on a particular line like this one...
arrayDetPerformance = [[NSMutableArray alloc] initWithArray:arrayDetail];
... it is telling you that said object allocated on that line was leaked. It is not telling you that particular line was the cause of the leak. The leak is likely because you either over-retained it somewhere else or forgot to leak it.
You've said in several comments that you are "deallocating [something] in your dealloc". Show your dealloc method's implementation.
[arrayDetPerformance release]; is not written in your code;
So, its show memory leak.
Related
I'm still trying to wrap my head around iphone memory management. I have checked this with leaks but I want to make sure. Is this free of leaks?
NSMutableArray *array = [[NSMUtableArray alloc] init];
NSMutableString *str = [[NSMutableString alloc]];
[str appendstring:#"hi"];
[array addObject:str];
[str release]; //this is the bit I am most concerned about
...some processing of array occurs...
[array release];
Assuming your second line is actually this:
NSMutableString *str = [[NSMutableString alloc] init];
Then yes, this is free of leaks. When you add the string to the array, the array takes an ownership interest in the string, so the subsequent statement where you release your ownership of it is fine. It still exists in the array as expected.
When you release the array, it will take care of cleaning up its own references, including the one pointing to the string you put in it.
RULE OF THUMB, YOU MAY WRITE THIS ON STICKY NOTE AND STICK IT ON YOUR DESK
If you alloc, new, init or copying than you are the owner :)
You have to release it! no one will clean up for you.
** Example :
NSString *releaseMeLaterPlease = [NSString initWithString....];
If you create any other way such as in Example assume "bag" is some array,
NSString *dontReleaseMe = [bag objectAtIndex:0];
now, dontReleaseMe isn't create by alloc, new, init or copy so you don't release them. Some one will do it.
If you use autorelease after alloc and init than, OS will take care of releasing it.
MOST IMPORTANT: Now developer doesn't have to worry about these stuff!!! Hoooooray! Automatic Reference Counting is on from iOS5
However it is good to learn as not all devices has iOS5 :)
Good luck!
quixoto answered the question, but just for the sake of being explicit, here's what's going on with regard to memory management in your code on each line:
NSMutableArray *array = [[NSMUtableArray alloc] init]; //array retain count = 1
NSMutableString *str = [[NSMutableString alloc]]; //str retain count = 1
[str appendstring:#"hi"];
[array addObject:str]; //str retain count = 2
[str release]; //str retain count = 1
...some processing of array occurs...
[array release]; //array retain count = 0 & str retain count = 0 .. objects will be removed from memory.
I realize this question may sound dumb, but just bear with me. I built an app to help new developers wrap their head around memory retention on the iPhone (no ARC yet). It is plain and simple, 4 buttons, init, access, retain, and release. Pretty self explanatory. I am displaying what the retain count for my string object that is the target of our poking and prodding. (Please no lectures on use of [myVar retainCount], I already know)
This stuff will never make it into actual apps, just toying with it for fun and hopefully help someone learn how memory works. My retain and release all work great. My question is that why does my retain count drop back to 1 if I call myString = [[NSMutableString alloc] init]; again. I can boost my retain count to 40, but after calling alloc/init I go back to zero. I am not leaking anywhere, just curious what happens to myString if/when alloc/init is called on it again.
My question is that why does my retain count drop back to 1 if I call
myString = [[NSMutableString alloc] init]; again?
Because you are failing to understand a very basic concept of Objective-C; myString is not an instance of an NSMutableString, but a reference to an instance. If you were to:
myString = [[NSMutableString alloc] init];
myString = [[NSMutableString alloc] init];
You now have two instances of NSMutableString, one leaked.
If you:
myString = [[NSMutableString alloc] init];
otherString = myString;
You now have a single instance of NSMutableString with two references.
In all three allocations, the NSMutableString instance will have a +1 retain count and, thus, you must balance each with a single release or you'll leak.
Treating retain counts as an absolute count is a path to madness. Or, at best, the scope of usefulness of the absolute retain count is so limited that learning about it is not applicable to real world iOS programming.
This bears repeating:
The retainCount of an object is tricky business.
If you were to continue down this path, you should be aware of the following details:
retainCount can never return 0
messaging a dangling pointer is not guaranteed to crash
retain count cannot be known once you have passed an object through any system API due to implementation details
any subclass of any system class may have an unknown retain count due to implementation details
retain count never reflects whether or not an object is autoreleased
autoreleases is effectively thread specific while the retain count is thread global
some classes are implemented with singletons some of the time (NSString, certain values of NSNumber)
the implementation details change from platform to platform and release to release
attempting to swizzle retain/release/autorelease won't work as some classes don't actually use those methods to maintain the retain count (implementation detail, changes per platform/release, etc..)
If you are going to teach retain/release, you should be treating the retain count as a delta and focus entirely on "If you increase the RC, you must decrease it".
when you call myString = [[NSMutableString alloc] init];, you're not "calling alloc/init on it again". You're not calling a method on the same instance you had. You're allocating and initializing a new instance, a completely different object from the one you had before.
And if you're doing that with a variable that had an object that you retained, then yes, you are leaking it.
Try this.
NSString *myString = [[NSMutableString alloc] init];
NSLog(#"%d", [myString retainCount]); // "1"
for (int i = 1; i < 40; i++)
[myString retain];
NSLog(#"%d", [myString retainCount]); // "40"
NSString *backup = myString;
myString = [[NSMutableString alloc] init];
NSLog(#"%d", [myString retainCount]); // "1"
NSLog(#"%d", [backup retainCount]); // "40"
You see, you have a different object with a new retain count. Your original object still exists and still has the same retain count. Assignment changes the object a variable refers to. A variable doesn't have a retain count, an object does.
myString = someOtherString;
NSLog(#"%d", [myString retainCount]); // who knows?
With retained property:
self.iString = backup;
NSLog(#"%d", [self.iString retainCount]); // "41" - 1 more because property retained
NSString *newString = [[NSMutableString alloc] init];
NSLog(#"%d", [newString retainCount]); // "1"
self.iString = newString;
// 1 for alloc 1 for retain (in real code you should release newString next)
NSLog(#"%d", [self.iString retainCount]); // "2"
NSLog(#"%d", [backup retainCount]); // "40" - self.iString released it
According to the leak instrument in XCode it's saying this line is giving a memory leak (100%)?
self.unsentPatients = [[NSMutableArray alloc] initWithArray:[defaults arrayForKey:UNSENT]];
I'm correctly releasing etc. on dealloc (which is definitely being ran) so I don't understand where I am going wrong?
It's only a small leak and Analysis doesn't come up with anything, but nonetheless it's still a leak.
Kind regards,
Dominic
There are many things wring with this code.
I'm assuming that the property is retaining the value, then you should not assign the value the way you are doing now, but more like:
NSMutableArray *temp = [[NSMutableArray alloc] initWithArray:[defaults arrayForKey:UNSENT]];
self.unsentPatients = temp;
[temp release], temp = nil;
or
self.unsentPatients = [[[NSMutableArray alloc] initWithArray:[defaults arrayForKey:UNSENT]] autorelease];
You should also avoid using the self. syntax in dealloc or init, which will call a mutator.
In multithreaded environment this could give problems.
So the correct dealloc would be:
- (void) dealloc {
[unsentPatients release], unsentPatients = nil;
[super dealloc][;
}
I am developing an iphone app. Instruments reported a leaked object ServiceTypes. Below is the relevant code. Does anyone have any ideas? Thanks a lot for your help.
ServiceTypes *serviceTypes = [[ServiceTypes alloc] init];
if ([userConnection getServiceTypes:serviceTypes]) {
if ([serviceTypes.types length] > 0) {
NSArray *array = [[NSArray alloc] initWithArray:[serviceTypes.types componentsSeparatedByString: SERVICE_TYPE_DELIMITOR]];
serviceRequestTypes = [[NSMutableArray alloc] initWithArray:array];
[array release];
}
}
[[self typesTableView] reloadData];
[serviceTypes release];
It doesn't look like serviceTypes is being leaked. From the code you posted, serviceTypes is always released at the end of the method, and it doesn't appear to be retained anywhere in your sample. My question is: what happens inside getServiceTypes:. Does that method retain the serviceTypes parameter?
One more thing. If serviceRequestTypes is an instance variable (and it looks like it is), then you may be leaking memory by reassigning it without releasing the existing serviceRequestTypes object first. You should either rewrite serviceRequestTypes to be a property and use a synthesized accessor or make sure to release it every time before assigning. If its current value is nil, no big deal; the release message will simply be ignored. For example:
[serviceRequestTypes release];
serviceRequestTypes = [[NSMutableArray alloc] initWithArray:[serviceTypes.types componentsSeparatedByString:SERVICE_TYPE_DELIMITER]];
I am in memory-leak cleanup mode on my latest app, and have come across something that I am unable to solve.
the following method has been cleaned up except for 1 nagging issue. Instruments tells me that my NSMutableArray called itemsToKeep is leaking memory, at the point that I am creating the object. Any ideas on why I am leaking memory would be most appreciated.
Here are some notes on retainCounts:
entering the method: self.myList has retainCount = 1
exiting the method: self.myList has retainCount = 2 and itemsToKeep has retainCount= 2.
I can easily do a [itemsToKeep release] at the end which brings both counts down to 1, but the app crashes after a while (and I think I know why).
Does anyone know how I can get rid of the memory leak for itemsToKeep?
Thanks.
-(void)parsedScores:(BOOL)shouldAdd {
//trim space, tab, newline from both ends
NSString *tmp = self.lblCurrentName.text;
NSString *list = [self trimString:tmp];
NSString *separators = #",";
[self.myList removeAllObjects]; // doesn't impact retain counts
self.myList = (NSMutableArray *)[list componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:separators]]; //this bumps up the self.myList retain count to 2
NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:30];
for (NSString *item in self.myList) {
NSString *tmpItem = [self trimString:item];
if (! [self shouldRemoveItem:tmpItem]) {
[itemsToKeep addObject:tmpItem];
}
}
self.myList = itemsToKeep; //makes both variables' retain counts = 2
}
I can't see a leak in the method you've provided, so I assume it's happening elsewhere. You should check if self.myList is retained somewhere else without it being released.
Also, you probably shouldn't be looking at the retain count for debugging purposes. The retain count can be misleading because it doesn't matter how many times an object is retained as long as it's released an equal amount of times.