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.
Related
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
Removed release statements. Some of them seemed to be okay, but that was probably just because other things were exploding first.
- (void)handleNowPlayingItemChanged:(id)notification {
MPMediaItem *item = self.musicPlayer.nowPlayingItem;
NSString *title = [item valueForProperty:MPMediaItemPropertyTitle];
NSNumber *duration = [item
valueForProperty:MPMediaItemPropertyPlaybackDuration];
float totalTime = [duration floatValue];
progressSlider.maximumValue = totalTime;
CGSize artworkImageViewSize = self.albumCover.bounds.size;
MPMediaItemArtwork *artwork = [item valueForProperty:
MPMediaItemPropertyArtwork];
if (artwork) {
self.albumCover.image = [artwork imageWithSize:artworkImageViewSize];
} else {
self.albumCover.image = nil;
}
titleLabel.text = title;
/*OpenEars stuff*/
}
In another question I mention the SQLite errors concerning artwork.
** Deleted error and details concerning NSZombieEnabled alert of call to released objects. **
Well don't I feel stupid. It was all memory management.
I put effort into not leaking anything, even in a temporary solution, and yet I did this...
In the code you provide I do not see any calls to retain, alloc/init, or some variation of copy. That means that you should not have a any calls to release in that method and that will be the cause of your crash. Make sure you are not over releasing in other methods and remember the basics of memory management.
You're releasing title and artwork, but they're not yours. This will lead, soon or later, to a tentative to release an already deallocated object (from item's dealloc or somewhere else).
// [artwork release];
//[title release];
comment those since those are autoreleased object
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.
I'm having the code as below.
- (void)viewDidLoad
{
NSArray* myarr = [self createArray];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
[myarr release];
}
-(NSArray*)createArray
{
NSArray* arr1 = [[NSArray alloc] initWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
return arr1;
}
When I "Build & Analyze", its showing two leaks. One at [myarr release] saying, incorrect decrement of the reference count of an object that is owned at this point. and Other at return arr1, saying, Potential leak of an object allocated on line 152 and stored into arr1.
From my above code, the method "createArray" is returning a pointer and I'm releasing it as well. Is my way of coding right or wrong?
From my above code, the method "createArray" is returning a pointer and I'm releasing it as well. Is my way of coding right or wrong?
that depends on how you look at it.
1) the ref counting looks ok
2) the static analyzer flags objc methods based on names, in some cases. so the issue will likely vanish if you rename createArray to newArray, or something named new*. so it expects a convention (the ones used by Apple) to be followed.
therefore, it's the message that's bit shallow, it doesn't really analyze the program, but bases its findings/results on convention -- and not an actual evident issue which a human can read.
If you're just using the array in your viewDidLoad method, then you don't need to alloc an array in there at all. You can just use an autoreleased array returned as 7KV7 suggested. You can return an autoreleased array in your -(void)createArray as well without alloc'ing an object. Here is an example.
- (void)viewDidLoad
{
NSArray* myarr = [self createArray];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
}
-(NSArray*)createArray
{
return [NSArray arrayWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
}
If you don't have to alloc an object to use it, it makes for less, and cleaner code, IMO.
Try this
- (void)viewDidLoad
{
NSArray* myarr = [[NSArray alloc] initWithArray:[self createArray]];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
[myarr release];
}
-(NSArray*)createArray
{
NSArray* arr1 = [[NSArray alloc] initWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
return [arr1 auotrelease];
}
The problem with your code is that
You do not allocate myarr using alloc or new so you do not take ownership of the object. Hence the issue in release.
You allocate arr1 so you take ownership of the object and you return arr1. Hence you do not release it. That is the reason for the leak.
I was running Leaks tool and discovered a massive leak in my Dictionary mutableDeepCopy but I can't figure out what's wrong with the code. Any suggestions?
#interface RootViewController : UIViewController{
NSDictionary *immutableDictionary;
NSMutableDictionary *mutableDictionary;
}
Here is the line of code that's highlighted in Instruments
self.mutableDictionary = [self.immutableDictionary mutableDeepCopy];
Here is the method for creating a mutable copy of a Dictionary
#interface NSDictionary(MutableDeepCopy)
-(NSMutableDictionary *)mutableDeepCopy;
#end
Here is method implementation, I've highlighted the code that Leaks saids is leaking 100%
- (NSMutableDictionary *) mutableDeepCopy {
NSMutableDictionary *dictionaryToReturn = [NSMutableDictionary dictionaryWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for(id key in keys) {
id value = [self valueForKey:key];
id copy = nil;
if ([value respondsToSelector:#selector(mutableDeepCopy)]) {
copy = [value mutableDeepCopy];
} else if ([value respondsToSelector:#selector(mutableCopy)]) {
copy = [value mutableCopy]; //This is the Leak
}
if (copy == nil) {
copy = [value copy];
}
[dictionaryToReturn setValue:copy forKey:key];
}
return dictionaryToReturn;
}
You need to analyse this in light of Apple's Memory Management Rules.
Starting with this line:
self.mutableDictionary = [self.immutableDictionary mutableDeepCopy];
I would expect mutableDeepCopy to return an object I own, so at some point I need to release or autorelease it. e.g.
NSMutableDeepCopy* temp = [self.immutableDictionary mutableDeepCopy];
self.mutableDictionary = temp;
[temp release];
or
self.mutableDictionary = [[self.immutableDictionary mutableDeepCopy] autorelease];
So now we need to look at mutableDeepCopy. Because it has 'copy' in the name it needs to returned an "owned" object which, in practice means "forgetting" to release the returned object. You have already failed to do that when you create the returned object in the first line, since dictionaryWithCapacity: gives you an object you do not own. Replace it with
NSMutableDictionary *dictionaryToReturn = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
Now you own it.
It is important that you make your mutableDeepCopy obey the rules because it means you can treat the objects returned from mutableDeepCopy, mutableCopy and copy in exactly the same way. In all three cases you own the object copy that you insert into the array. Because you own it, you must release it or it'll leak as you found out. So, at the end of the loop, you need
[copy release];
That'll stop the leak.
How is your property declared? If is is retain or copy, then this doesn't leak.
Your problem is that the name mutableDeepCopy suggests that it returns a retained object, and not an autoreleased one as it actually does.
Edit:
And at the mutableDeepCopy itself, you need to release the copy variable after adding to the dictionary.
mutableCopy increments the retain count of the object, as does setValue:forKey:. This means that when dictionaryToReturn is dealloc'ed, the object that had mutableCopy called still has a retain count of one.
Try doing this instead:
copy = [[value mutableCopy] autorelease];