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
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 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 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.
I have a very clear question:
//.h file
#property (nonatomic, retain)NSMutableString * retainString;
#property (nonatomic, copy)NSMutableString * copyString;
//.m file
#synthesis retainString, copyString;
-(void)Process
{
NSMutableString *test = [[NSMutableString alloc]inti];//retain count should be 1
self.retainString = test;
self.copyString = test;
}
cond. 1-> // retain count of both should be 2. As they are pointing to the same memory location with retain count 2 so how may release should be written.
cond. 2-> // //retain count of test is 1 and copyString is 2. As both hold different memory location. but can we write [copyString release].
This setup actually does some very interesting things, and raises a couple good points about Objective-C memory management. Let's first reiterate the code:
// Testing.h
#interface Testing : NSObject {
NSMutableString *retainString;
NSMutableString *copyString;
}
#property(nonatomic,retain) NSMutableString *retainString;
#property(nonatomic,copy) NSMutableString *copyString;
// Testing.m
#implementation Testing
#synthesize retainString, copyString;
- (id)init {
if(self = [super init]) {
NSMutableString *test = [[NSMutableString alloc] init];
NSLog(#"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
self.retainString = test;
NSLog(#"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
self.copyString = test;
NSLog(#"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
[self.copyString appendFormat:#"test"];
NSLog(#"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
}
return self;
}
#end
This produces the log output:
2009-12-24 03:35:01.408 RetainCountTesting[1429:40b] test 1; retain 0; copy 0
2009-12-24 03:35:01.410 RetainCountTesting[1429:40b] test 2; retain 2; copy 0
2009-12-24 03:35:01.410 RetainCountTesting[1429:40b] test 2; retain 2; copy 2147483647
2009-12-24 03:35:01.413 RetainCountTesting[1429:40b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendFormat:'
So what's going on here? The first two calls are fairly straightforward:
The initial call to alloc/init creates a new NSMutableString object with retain count 1, as expected. We have one object with one retain on it.
The assignment to the retained property increments the retain count, as expected. We have one object with two retains on it.
Here's where it gets strange. The assignment to the copy property does indeed make a copy, but not in the way you'd expect. NSString and NSMutableString are part of what's called a class cluster - when you create or modify a string, it may or may not be an instance of the class you're expecting. The language may mutate it to some other representation behind the scenes.
In this particular case, when the copy is performed, apparently the language decides that the string (since it contains no information) is to be considered immutable, and makes it so. This is often seen when people do something like [[NSString alloc] initWithString:#"hello"] - it's a constant, static string, so no object need be allocated dynamically. Keeping it static helps the runtime perform better.
So now we have two objects: our original test object that was retained twice, and the new object that is static and therefore has a retain count of INT_MAX. Finally, since the new string is immutable, calling a mutator method on it kills the program.
As an aside, changing the original call from init to initWithString: does make the copy assignment perform (somewhat) as expected - you only get a retain count of 1 on the copied object, but you still can't mutate it. Again, this is probably due to some optimization magic inside the compiler that decided that string was static and saw no reason to make it mutable if it didn't have to.
To answer your final question: yes, you can call release on either of these objects. It just won't do much. At best, you'll have destroyed the copied object (since it had a retain count of 1); at worst, it'll do nothing to a static string object. However, I'd recommend continuing to work through properties: rather than releasing the copied object, why not just do self.copyString = nil;? Since it calls the property setter, it'll take care of the release as necessary, and then you don't have a pointer to the object still floating around.
For more information on all this, consider reading:
Memory Management Programming Guide for Cocoa
The NSString class reference
The Objective-C Programming Guide, Declared Properties section
If you have properties defined with keywords 'retain' or 'copy' you should always release corresponding member variables in the dealloc method.
In this case your dealloc should look like this:
- (void)dealloc {
[retainString release];
[copyString release];
[super dealloc];
}
Now, when you alloc a string in your custom class' method, this method owns the string as described in the Memory Management Programming Guide for Cocoa. This means that you should release the string before leaving the method.
#synthesize retainString, copyString;
- (void)Process {
NSMutableString *test = [[NSMutableString alloc] init]; //retain count is 1
self.retainString = test; // retain count of test is 2
self.copyString = test; // test's retain count = 2, copyString's = 1
[test release]; // retain count of test is 1 again
}
When an instance of this class is destroyed, the dealloc method will be invoked which will in turn release both strings. And as soon as their have retain counts remain 1, they will be dealloc'ed too.
This may also help
NSString property: copy or retain?
Whenever I read about how to avoid memory leaks, I always came across a concept that
"Number of alloc must be equal to number of release".
But I came across a concept where we require more than one release. Like What I used to practise was as follows:
(NSString*) func1
{
NSString* result = [[NSString alloc] initWithFormat:#"Test String"]];
return result;
}
(void) func2
{
NSString* temp = [self func1];
[temp release];
}
But I came across a concept of retain count which says that in the above case the memory is not deallocated for the string since the retain count for the string is 1 at the end. So the right practise is
(NSString*) func1
{
NSString* result = [[NSString alloc] initWithFormat:#"Test String"]];
[result autorelease];
return result;
}
(void) func2
{
NSString* temp = [self func1];
[temp release];
}
So now I have two releases for deallocating the memory which is a contradictory to my above sentence which I read on most of the blogs ""Number of alloc must be equal to number of release".
I am little bit confused about the above stuff. Becoz if I autorelease the string in the first function and want to use the string in second function for a long time, and what if the release pool is flushed in between, on the other side if I dont use autorelease it will still block the memory.
So whats the correct way of doing it.
At the time you call alloc whatever is returned will have a retainCount of 1. Calling release on that object will cause it to be deallocated (it's retainCount will drop to 0). In your first example, then, the second line of func2 will deallocate the NSString* you received from func1, and your memory management chores are complete.
In the second example you are tossing result in func1 into the current autorelease pool, which will cause it to become deallocated when the pool drains. You do not want to attempt to manage the memory of that object once it has been placed into the pool- it is no longer your responsibility.
If you want to generate the string and keep it around for a while (e.g., through the lifetime of several autorelease pools), I would recommend the first form of memory management.
The correct way is this:
(NSString*) func1 {
NSString* result = [[NSString alloc] initWithFormat:#"Test String"];
// retaincount == 1
return [result autorelease];
}
(void) func2 {
NSString* temp = [self func1];
// retaincount == 1
// temp is autoreleased, therefore no [release] is necessary.
}
Autorelease is automatically done at the end of the run loop, that means it cannot be emptied while your code is doing something. -> The code you have is safe. This isn't true for multithreaded application!
(NSString*) func1
{
NSString* result = [[NSString alloc] initWithFormat:#"Test String"]];
return result;
}
[result retainCount] is 1
(void) func2
{
NSString* temp = [self func1];
[temp release];
}
[temp retainCount] is 0
No need for autorelease.
From Memory Management Rules:
This is the fundamental rule:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
The following rules derive from the fundamental rule, or cope with edge cases:
As a corollary of the fundamental rule, if you need to store a received object as a property in an instance variable, you must retain or copy it. (This is not true for weak references, described at “Weak References to Objects,” but these are typically rare.)
A received object is normally guaranteed to remain valid within the method it was received in (exceptions include multithreaded applications and some Distributed Objects situations, although you must also take care if you modify the object from which you received the object). That method may also safely return the object to its invoker.
Use retain in combination with release or autorelease when needed to prevent an object from being invalidated as a normal side-effect of a message (see “Validity of Shared Objects”).
autorelease just means “send a release message later” (for some definition of later—see “Autorelease Pools”).
In general, I'd feel safer to do a retain on a return value, like the one in the "func 2":
(NSString*) func1 {
NSString* result = [[NSString alloc] initWithFormat:#"Test String"];
return [result autorelease];
}
(void) func2 {
NSString* temp = [[self func1] retain];
// Do something with temp
[temp release];
}
Is this unnecessary? I understand that in this example "temp" is just a local variable. But it could have been an instance variable, which may need to be retained.