I am learning Objective-C right now, however, there is memory management puzzle here make me so confused.
Let me see, within one method, I create a NSMutableString, and return it.
- (NSMutableString *)methodNameWithParameter:(id)parameter
{
NSMutableString *string = [NSMutableString stringWithString:#""];
// do something
return string;
}
The question is who is responsible to release this memory, calling or called?
Second example:
- (NSMutableString *)methodNameWithParameter:(id)parameter
{
NSMutableString *string = [NSMutableString alloc]init] autorelease];
// do something
return string;
}
When memory has been released, it will be released at after return string;
or it will be released at call method and there is no reference to it.
The third one:
- (NSMutableString *)methodNameWithParameter:(id)parameter
{
NSMutableString *string = [NSMutableString alloc]init]];
// do something
return string;
}
This time the calling method need to release this memory, is that right?
If you follow the rule, you allocated memory then you are responsible to release it. 90% of time this will work. Of course there are some exception. But in general it should be good.
In your first example, you don't have to release it because you didn't allocate memory yourself, it's the stringWithString that is responsible (I believe it's doing an auto release)
In your second and third example, you are allocating memory with alloc, thus you have to release the memory once you are done with it.
In you second example, you are using autorelease, it means it the memory allocated will eventually be released. (similar to garbage collection in the Microsoft managed code world).
Related
I'm curious about the memory I'm allocating with my factory method. The idea is, I'll read in a file, and I'll instantiate objects by sending a single unparsed CSV line to my factory method, as such:
-(id)initWithCSV:(NSString *)csv
{
if(self = [super init])
{
NSArray *values = [csv componentsSeparatedByString:#","];
self.city = [values[0] stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
self.country = [values[1] stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
self.latitude = [values[2] doubleValue];
self.longitude = [values[3] doubleValue];
}
return self;
}
My first question is, how is this on memory management? I'm not completely clear on how ARC works. My values array is destroyed as soon as I return self; and exit the method, right?
Second question... is there a more efficient way to accomplish what I'm trying to do here?
In regards to your first question:
In this case, ARC doesn't actually make a difference. Since the commentsSeparatedByString: method already returns an auto-released object, and you aren't increasing it's retain count (or in ARC terms, creating any strong references), it will be cleaned up automatically when the method returns.
As for efficiency, you could probably save some memory by using NSScanner to scan up to each comma, instead of creating an array with a bunch of strings. But since this is only a few strings that are relatively short, in practice it doesn't really matter.
TL/DR: If you're using ARC, your method is very memory efficient.
This actually showcases an interesting feature of ARC, having to do with the fact that the output of componentsSeparatedByString: is autoreleased.
Say you had this function:
while ((line = [self readLine])) {
[self.lines addObject:[[MyObj alloc] initWithCSV:line];
}
Without ARC, this seemingly efficient function could consume a lot of memory because the object in values is retained until the autorelease pool is drained. (Same with line above actually.) One traditional way to optimize this is to do as follows:
while ((line = [self readLine])) {
#autoreleasepool {
[self.lines addObject:[[MyObj alloc] initWithCSV:line];
}
}
This way, when you hit the the end of the pool, the autoreleased values is deallocated. You of course, take a performance hit for draining the pool.
An naive ARC implementation would not solve this problem. It would see the +0 output of componentsSeparatedByString:, call [values retain] and call [values release] at the end of scope.
What ARC actually does is call a function objc_retainAutoreleasedReturnValue(values) which instead of retaining values, removes it from the autorelease pool. That way when it calls [values release] at the end of scope, the array is deallocated. Likewise, it smartly releases line at each iteration of my loop.
This provides excellent memory and CPU efficiency.
I think that NSArray is released since the scope is limited in the init due to autorelease pools. But the values are copied in self.latitude for example.
I have this code of string and I have problems trying to free up memory, I have understood that only those who release it initializes and is not autorelease but I had problems with the string "end", and as nSum release.
NSString *urlBase = [[NSString alloc] initWithFormat:#"http://service.svc/"];
NSString *op = [[NSString alloc] initWithFormat:#"op1"];
NSString * final = [urlBase stringByAppendingFormat:op];
NSString * nSum = sumTextfield.text;
final = [final stringByAppendingFormat:nSum];
//release
[ urlBase release ];
[ op release ];
//[final release]; error
//[final autorelease]; error
thank for you help.
UPDATE:
- (IBAction)mostrarOpciones {
// code (UP)
}
If you create an object using a method that begins with init, new, copy, or mutableCopy, then you own that object and are responsible for releasing it (or autoreleasing it) when you're done with it. If you create an object using any other method, that object is autoreleased, and you don't need to release it. In that case, you actually need to retain the object if you want to keep it around. Apple has a Memory Management Programming Guide that includes all these rules.
The code you've posted is actually correct. You need to release urlBase and op because you created them using a method beginning with init (initWithFormat: in this case). final and nSum are already autoreleased for you. final was created by a method that doesn't begin with init, new, copy or mutableCopy (in this case, the factory method stringByAppendingFormat:). nSum was returned by a method called text, and you can assume that sumTextField "owns" it or has autoreleased it before returning it to you, and so you're not responsible for releasing it.
you cannot release NSString which you did not allocate. Since your two variables are not allocated, they need not to be released.
When using the XCode analyzer I get a message saying:
Potential leak of an object allocated
The code this is in my NSData(String) category, the code is:
- (NSString*) utf8String
{
return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];
}
Now how can I solve this? When I change the statement to:
- (NSString*) utf8String
{
return [[[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding] autorelease];
}
My application crashes on the line where I call utf8String.
The cocoa naming conventions suggest that all methods return autoreleased objects, with the exception of methods whose names start with 'init', 'copy' or 'new'. The static analyzer knows and checks this.
You have two choices. You can rename the method to -newUTF8String, or you can return an autorelease object and retain it when you want to store the return value of this method.
I would prefer the latter, but both would be valid code.
I guess your application crashes because the variable is released before it is used. It is recommended to call retain if you do not use the return value right away but store it in a member variable.
...
myMemberVariable = [something utf8String];
[myMemberVariable retain];
...
To make sure that you do not produce a memory leak you have to release the member variable somewhere. A good place for that would be dealloc.
- (void)dealloc {
if (myMemberVariable) [myMemberVariable release];
[super dealloc];
}
I would also recommend having a look at Advanced Memory Management Programming Guide to get some detailed information about memory management of iOS.
I am facing some strange memory leak in our existing iPad application,
Here is a function which gives memory leak in instrument
-(NSString *)retriveInfo:(NSString*)fromstring:(NSString*)searchstring
{
NSArray *arrRetrive = [fromstring componentsSeparatedByString:searchstring];
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1){
NSString *strDisplayOrder = [arrRetrive objectAtIndex:1];
arrRetrive = [strDisplayOrder componentsSeparatedByString:#"<"]; //MEMORY LEAK
}
}
return [arrRetrive objectAtIndex:0];
}
Here is a input parameter
Param 1 : <displayorder>1</displayorder><filename>201103153_0100.pdf</filename><title>【1面】東日本巨大地震直撃、日経平均一時675円安[br]原発関連売られる、東電はS安</title><category>トップ・注目株</category><dirpath>/var/www/mssite/webapp/data/pdf/20110315</dirpath><TimeStamp>201103141700</TimeStamp><FirstPageImg>20110315top.png</FirstPageImg></pagedata>
Param 2: <displayorder>
Basically i want to found (parse) value between start and end tag.
(I know NSXMLParser class but asper i explain this one is existing code and if i changed in code its too much time consuming)
Any suggetion?
With Regards
Pankaj Gadhiya
The code you've posted doesn't look like it's leaking memory -- all the methods you're calling are of the autorelease type (i.e. there's no new, alloc, copy, or retain in the code).
It's probably the code you have that calls retrieveInfo and does something with the result that's leaking memory (e.g. overretaining it). The leaks tool is pointing you at the componentsSeparatedByString because that's where the memory was allocated which was eventually involved in a memory leak.
Can you show us how you call retrieveInfo and what you do with the result?
Btw, what's the point of this nested if?
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1)
It's wasteful, you might as well write this and get the same effect:
if ([arrRetrive count] > 1)
That leak probably means that you're over-retaining the return value. And leaks-tool just shows you the place where it was created.
You are leaking the NSArray when you re-assign a new NSArray inside the if block. The pointer to the original array is lost, which means that the runloop cannot release the memory allocated for the first result. The memory allocated for the second result is released.
You should really use a proper parser... however to address the leak, the following will work.
-(NSString *)retriveInfo:(NSString*)fromstring:(NSString*)searchstring
{
NSArray *arrRetrive = [fromstring componentsSeparatedByString:searchstring];
NSString *result = nil;
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1){
NSString *strDisplayOrder = [arrRetrive objectAtIndex:1];
result = (NSString *)[[strDisplayOrder componentsSeparatedByString:#"<"] objectAtIndex:0];
}
}
return result;
}
What do you do with the object that's returned by this method? [arrRetrive objectAtIndex:0] is likely what's being leaked.
Leaks indicates that line with componentsSeparatedByString: because that's where the strings in the array are allocated. But the array isn't leaked.
Go into Leaks, look at a leaked instance, and click that little arrow button. You'll see all the retains and releases of the leaked object, which should point you to the problem.
How do you release the memory in this situation? Do you have to?
- (NSString *) whatHappensHere {
NSMutableString * mutableString = [[NSMutableString alloc] initWithString:#"Hello"];
// ....
// more code ...
// ...
return mutableString;
}
With autorelease
- (NSString *) whatHappensHere {
NSMutableString * mutableString = [[NSMutableString alloc] initWithString:#"Hello"];
[mutableString autorelease];
return mutableString;
}
As willcodejavaforfood said, the convention is that any newly-allocated object returned from a method should be autorelease'd before being returned. This tells the Objective-C runtime that if nobody takes ownership of it with retain before (in most cases) the current iteration of the application event loop ends, it should be freed.
If it's just used locally in the calling function or returned up the stack, that works great and it gets freed sooner or later. If someone ends up wanting to keep it around, then they have to retain it and so will know they have to release it themselves later on.
(In fact, most of the non-init* utility constructors for ObjC base classes like strings, etc, do just that, or at least something functionally equivalent.)