I work on a project on iPhone iOS with Xcode 4.
With Xcode > Product >Analyze I get 35 issues, all of this type:
myTextField.text = [[NSString alloc] initWithFormat:#"0.2f", abc];
and the problem is "Potential leak of an object allocated at ..."
What is the offending object and how can I release it?
Thanks
You're leaking the string that you're assigning to myTextField.text. When this assignment happens, a copy is being made (look at the property definition in the documentation). In most cases, when values are immutable, as is the case with NSStrings, a copy will give you an instance that points to the same location as the object that is being copied, with the retain count incremented by 1.
In the case of your code:
myTextField.text = [[NSString alloc] initWithFormat:#"0.2f", abc];
The retain count of the string that you've allocated is 2.
You will either need to (1) release, (or autorelease) the string, or (2) use one of the NSString convenience methods, e.g. stringWithFormat: to create the string. This will give you an autoreleased string so you won't have to worry about explicitly releasing it.
(1)
NSString *str = [[NSString alloc] initWithFormat:#"0.2f", abc];
myTextField.text = str;
[str release]
or
myTextField.text = [[[NSString alloc] initWithFormat:#"0.2f", abc] autorelease];
(2)
myTextField.text = [NSString stringWithFormat:#"0.2f", abc]; // autoreleased
You are responsible for releasing string object you create here - as you use alloc/init for that.
The most convenient way here to set a string is to use class method +stringWithFormat that returns autoreleased string - so system will release that string object for you later:
myTextField.text = [NSString stringWithFormat:#"0.2f", abc];
Or you can write autorelease explicitly if you want:
myTextField.text = [[[NSString alloc] initWithFormat:#"0.2f", abc] autorelease];
If you don't want to use autorelease you can use temporary variable to create new string and release it after it was set for text field:
NSString *tempString = [[NSString alloc] initWithFormat:#"0.2f", abc];
myTextField.text = tempString;
[tempString release];
The thing is that UiTextFields's text property is declared as:
#property(nonatomic, copy) NSString *text
Therefore in this line:
myTextField.text = [[NSString alloc] initWithFormat:#"0.2f", abc];
A new NSString is created with a retain count of 1, and then myTextField.text copies this object and increased its retain count by 1 or does it??, lets see what is happening:
A NSString object created with alloc initWithFormat with a retain count of 1
A NSString object with is a copy of the previous String, but because NStrings are immutable in this case, copy returns the same object!, therefore the NSString actually has a retain count of 2.
Related
I have an NSTimer that fires once per second.
And every second I have an NSString that needs to be changed.
I've never tried to deal with memory management before so I'm not sure if what I'm doing is right but instruments is saying under "alloc" that the line of code with stringByReplacingOccurrencesOfString has 45MB of "Live Bytes" after about a minute...
(and the live byte count keeps on rising with every second and eventually crashes the app).
I think my issue lies somewhere with the MutableCopy code?
Here is my code:
-(void)myTimer {
if (testedit) {
[testedit release];
[withString1a release];
[forString1a release];
}
testedit = [[NSString alloc] init];
withString1a = [[NSString alloc] init];
forString1a = [[NSString alloc] init];
testedit = [[NSString alloc] initWithFormat:#"example"];
withString1a = [[NSString alloc] initWithFormat:#"e"];//this string gets its values randomly from an array in my real code
forString1a = [[NSString alloc] initWithFormat:#"flk34j"];//this string gets its values randomly from an array in my real code
testedit = [[testedit stringByReplacingOccurrencesOfString:withString1a withString:forString1a] mutableCopy];//memory leak /:
}
You are allocating memory for each object twice. When you alloc the second time and assign it to the same variable, the first piece of alloc'd memory becomes inaccessible and unreleasable.
Then you make a mutableCopy of testedit and assign the copy to the original's variable. Again, you leave a piece of inaccessible memory floating around.
The rule with non-ARC memory management is - for every alloc, new, copy or retain you need to have a corresponding release. You have 6 allocs, one copy, and only 3 releases.
Here are some suggestions.
Remove these duplicated allocations:
testedit = [[NSString alloc] init];
withString1a = [[NSString alloc] init];
forString1a = [[NSString alloc] init];
Presumably testedit, withString1a and forString1a are all iVars. (Please declare your iVars as autosynthesized properties and refer to them as self.testedit ... etc. that will make your code so much clearer to stack overflowers).
Take out all of this:
if (testedit) {
[testedit release];
[withString1a release];
[forString1a release];
}
Assuming these are all iVars, the correct place to release them is in your object's dealloc method
In fact withString1a and forString1a can be local variables, as you get their content from elsewhere:
NSString* withString1a = [[[NSString alloc] initWithFormat:#"e"] autorelease];
NSString* forString1a = [[[NSString alloc] initWithFormat:#"flk34j"] autorelease];
You can autorelease them as you don't need them to hang around after the method has finished.
These lines can also be written:
NSString* withString1a = [NSString stringWithFormat:#"e"];
NSString* forString1a = [NSString stringWithFormat:#"flk34j"];
(-stringWithFormat is a convenience method that returns an autoreleased object)
That leaves us with these two lines.
testedit = [[NSString alloc] initWithFormat:#"example"];
testedit = [[testedit stringByReplacingOccurrencesOfString:withString1a
withString:forString1a] mutableCopy];
It's not clear why you are treating testedit as an immutable string in the first line and a mutable string in the second. You don't need a mutable string here at all, as you are replacing testedit with a new string.
self.testedit = [[NSString alloc] initWithFormat:#"example"];
self.testedit = [[testedit stringByReplacingOccurrencesOfString:withString1a
withString:forString1a] copy];
(you need copy as stringByReplacingOccurrencesOfString:withString: returns an autoreleased object, and here you want to keep hold of it)
THE last piece of the jigsaw is getting rid of your _testedit iVar memory allocation. You do this in the dealloc method of your object:
- (void) dealloc {
[_testEdit release];
[super dealloc];
}
(Note that init, accessor, and dealloc methods are the three places where you should not refer to an iVar using property syntax.)
All good, but really, you should be using ARC! You are _far_more likely to introduce memory bugs this way than if you rely on the compiler to manage memory for you.
I would suggest you to make use of #property here.
In .h file declare the properties as:
#property (nonatomic, retain) NSString *testedit;
#property (nonatomic, retain) NSString *withString1a;
#property (nonatomic, retain) NSString *forString1a; //if required write the #synthesize as well in .m class
You can write your timer method as:
-(void)myTimer {
self.testedit = #"example";
self.withString1a = #"e";//this string gets its values randomly from an array in my real code
self.forString1a = #"flk34j";//this string gets its values randomly from an array in my real code
self.testedit = [self.testedit stringByReplacingOccurrencesOfString:self.withString1a withString:self.forString1a];
}
In dealloc method, you can set all the above properties as nil (self.testedit = nil;) or do a release on them([testedit release];).
If possible, try to switch to ARC, you dont have to worry about the memory management. The problem with your code was that you are using a lot of alloc/init statements without releasing the variable before doing it. This causes it to lose the reference of that variable and you will leak it. You dont need that many allocation statements. For every allocation or retain, there should be a corresponding release/auto-release statement.
If you're using ARC you shouldn't have an issue. If you aren't using ARC you can try adding autorelease:
testedit = [[[testedit stringByReplacingOccurrencesOfString:withString1a withString:forString1a] mutableCopy] autorelease];
You are getting a memory leak because you never de-allocate testedit. Whenever you call alloc, that means you need to deallocate it. This usually just means calling release.
Do something like this instead, then be sure to free up the memory you've allocated:
NSString* newString = [[testedit stringByReplacingOccurrencesOfString:withString1a withString:forString1a] mutableCopy];
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.
#define kTestingURL #"192.168.42.179"
...
NSString *serverUrl = [[NSString alloc] initWithString:
[NSString stringWithFormat:#"http://%#", kTestingURL]];
NSLog(#"retain count: %d",[serverUrl retainCount]);
Why is the retain count 2 and not 1?
Yes, You will get retain Count 2, one for alloc and other for stringWithFormat. stringWithFormat is a factory class with autorelease but autorelease decreases retain count in the future.
You shoud not care about the absolute value of the retain count. It is meaningless.
Said that, let's see what happens with this particular case. I slightly modified the code and used a temporary variable to hold the object returned by stringWithFormat to make it clearer:
NSString *temp = [NSString stringWithFormat:#"http://%#", kTestingURL];
// stringWithFormat: returns an object you do not own, probably autoreleased
NSLog(#"%p retain count: %d", temp, [temp retainCount]);
// prints +1. Even if its autoreleased, its retain count won't be decreased
// until the autorelease pool is drained and when it reaches 0 it will be
// immediately deallocated so don't expect a retain count of 0 just because
// it's autoreleased.
NSString *serverUrl = [[NSString alloc] initWithString:temp];
// initWithString, as it turns out, returns a different object than the one
// that received the message, concretely it retains and returns its argument
// to exploit the fact that NSStrings are immutable.
NSLog(#"%p retain count: %d", serverUrl, [serverUrl retainCount]);
// prints +2. temp and serverUrl addresses are the same.
You created a string and then used it to create another string. Instead, do this:
NSString *SERVER_URL = [NSString stringWithFormat:#"http://%#", kTestingURL];
this is because you [[alloc] init] a first NSString so serverUrl have retain +1 and at the same line you call [NSString stringWithFormat] that return another nsstring on autorelease with retain count at 2
you should only use the :
NSString *serverUrl = [NSString stringWithFormat:#"http://%#", kTestingURL];
so your serverUrl will have retainCount to 1 and you don't have to release string
Hi I generally create objects of another classes. can you please tel me if this wil be in the auto release pool? or should we release it manually.
if you init copy or new them you'll have to deallocate them if you put an autorlease with the allocation then they will be autoreleased
for example
Foo *foo = [[Foo alloc] init]; //you'll have release it somewhere yourself
And
Foo *foo = [[[Foo alloc] init] autorelease];// this will be autreleased
The simple case is : if you use init, you are responsible for releasing it, either by calling release or by calling autorelease.
e.g.
NSString *myString = [NSString alloc] init]; // You need to release this
...
[myString release]; // Now it's released - don't use it again!
or if you are going give it to someone else
NSString *myString = [NSString alloc] init]; // This needs releasing
...
return [myString autorelease]; // You are finished with it but someone else might want it
However, there's a few other cases.
NSString *myString = [NSString stringWithFormat:#"hi"];
This object is in the autorelease pool already - don't release it!
NSString *secondString = [myString copy];
This object needs releasing - it is not autoreleased.
Rule of thumb : Anything with init, copy or new in the name - you made it, you release it. Anything else will be autoreleased.
For some reason when I release the NSArray I get the EXC_BAD_ACCESS exception. Here is the implementation:
-(void) loadAllAlphabets
{
NSBundle *bundle = [NSBundle mainBundle];
NSArray *imagesPath = [[NSArray alloc] init];
imagesPath = [bundle pathsForResourcesOfType:#"png" inDirectory:#"Images"];
alphabets = [[NSMutableArray alloc] init];
NSString *fileName = [[NSString alloc] init];
for(int i=0; i<= imagesPath.count -1 ; i++)
{
fileName = [[imagesPath objectAtIndex:i] lastPathComponent];
CCSprite *sprite = [CCSprite spriteWithFile:fileName];
sprite.userData = [[fileName stringByDeletingPathExtension] uppercaseString];
[alphabets addObject:sprite];
}
// release fileName
[fileName release];
fileName = nil;
[imagesPath release]; // this causes the application to crash with EXC_BAD_ACCESS
// imagesPath = nil;
}
UPDATE 1:
So, the problem was that although I was responsible for releasing the imagesPath object since I used alloc that soon become irrelevant when pathsForResourcesOfType returned an autorelease object.
This means I should not release the imagesPath object manually.
The following line should be used:
NSArray *imagesPath = [bundle pathsForResourcesOfType:#"png" inDirectory:#"Images"];
UPDATE 2:
Another question which is related to this post. In the following code I initialize a new NSMutableArray manually.
alphabets = [[NSMutableArray alloc] init];
Later I insert CCSprite (Cocos2d objects) into alphabets array. CCSprite are autorelease objects. Do I still have to release alphabets manually? Since, after some time all objects are released and memory will be returned but then what will be left inside alphabets NSMutable array?
I think the confusion is here:
NSArray *imagesPath = [[NSArray alloc] init];
imagesPath = [bundle pathsForResourcesOfType:#"png" inDirectory:#"Images"];
The first line creates a new object. This object really needs to be released.
The second line over-writes that object with a new, self-managed object. This does not need to be manually release.
This means that you're leaking the first imagesPath.
In general, you need to release an object if you alloc or copy it. And you shouldn't over-write an object before you release (or autorelease) its content.
general rule of thumb in memory management - you should release an object only if you obtain it using method that contains new, copy or alloc in it (standard method follow that rule and you should stick to it as well).
In your case you obtain imagesPath object using pathsForResourcesOfType: method which returns an autoreleased object so you must not release it yourself.
Edit: yes, you need to release alphabets object somewhere (for the same reason - you got it with alloc method).
Objective-c containers take an ownership of objects added to them, that us when objects are added to an array they get retained so it is guaranteed that their life time is at least as long as the life time of container. When you remove object from collection or collection itself is destroyed then its members get released (to compensate retain on add).
Also, you are leaking memory as you initialize imagesPath with an empty non-mutable array and then discard it when you assign he result of pathsForResources: to it. Just do this instead:
NSArray *imagesPath = [bundle pathsForResourcesOfType:#"png" inDirectory:#"Images"];
Same error with fileName. Not need to initialize it with an empty non mutable string.
And also do not release fileName since it is also an autoreleased object.