I have an app that leverages the TouchJSON objective-C library and I'm running the Instruments profiler for memory leaks and getting a leak in that source that I can't figure out how to fix. I should mention that I'm fairly new to Cocoa and objective-C. Instruments is showing that the leak occurs in a method with the following signature:
- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError
...and the leak is specifically occurring in this block of code:
if (self.options & kJSONScannerOptions_MutableLeaves)
{
*outStringConstant = [theString autorelease];
}
else
{
*outStringConstant = [[theString copy] autorelease]; //LEAK IS HAPPENING HERE
[theString release];
}
I've tried a variety of fixes to try and get rid of the leak but with no success. Can someone please educate me on:
1) Why this is a leak
...and...
2) How to fix it
I'm familiar with the rudiments of objective-C memory management ("If you alloc, copy, or new...release is up to you") so I don't need a whole primer on the basics - just some insight as to why this is leaking.
Thanks in advance for any help.
EDIT: Attaching image of debug info.
What Instruments is telling you isn't that the leak occurred at that line, instead it's telling you that the object created at that line was leaked.
I would look again at the client code that uses the scanJSONStringConstant method and review its memory management. Is there a logical flow where it may miss a release call on the outStringConstant pointer?
change this line *outStringConstant = [[theString copy] autorelease];
TO
if(*outStringConstant)
[*outStringConstant release];
*outStringConstant = [NSString stringWithString:theString];
[theString release];
Related
Thanks for stopping by.
this static analyser warning is annoying me
here is my code
+ (INURLConnection*)createConnectionForRequest:(INHTTPRequest *)aRequest {
INURLConnection* result = [[INURLConnection alloc] init];
if(result){
result.request = aRequest;
result.error = nil;
}
return result;
}
the "return result" line is showing me the potential memory leak warning (when i use analyser). This is a create method, the methods that consumes looks like this
INURLConnection *connection_tmp = [INURLConnection createConnectionForRequest:aRequest];
self.connection = connection_tmp, [connection_tmp release];
so the create connection returns with retain count of 1,
self.connection increases the retain count, and also there is a release.
i know i can use ARC, but i want to know why the static analyser is showing potential memory leak here.
Is it safe to ignore this warning?
cheers
Arun
It's not safe to ignore this warning because you are violating one of the "rules" of Cocoa memory management: "You Don’t Own Objects Returned by Reference".
Change the last line of your function to
return [result autorelease];
and the Xcode warning should go away.
You should read Apple's documentation on the subject, Basic Memory Management Rules, which go in to the various conventions in a bit more detail. It's good to stick to these rules as anyone else reading or using your code will expect it to behave that way.
I ran into this problem while trying to fix a memory leak with the facebook-ios-sdk. How do i handle this situation when passing objects from no arc compiled classe to arc enabled classe?
This is the code inside the non arc compiled Facebook library: (i removed the unnecessary stuff which is not related to the problem) as you can see, result object is not autoreleased or released.
- (void)handleResponseData:(NSData *)data {
NSError* error = nil;
id result = [self parseJsonResponse:data error:&error];
self.error = error;
// Call the defined delegate wich is my AppDelegate didLoad method wich is arc enabled
[_delegate request:self didLoad:result];
}
- (id)parseJsonResponse:(NSData *)data error:(NSError **)error {
SBJSON *jsonParser = [[SBJSON alloc] init];
//gets the object wich leaks or gets overreleased
id result = [jsonParser objectWithString:responseString];
[jsonParser release];
return result;
}
Now if i try to add autorelease to the result object, i am facing a NSZombie when my arc code in my AppDelegate try's to release the object. However if i leave the code like this i'm facing memory leaks whit the result object which gets not released.
am i missing something basic? i can't get my head around this?
Thanx for any advice! Chris
The result returned from -parseJsonResponse:... is autoreleased already (see note at bottom).
Since the name of the -parseJson method doesn't begin with new, alloc, copy or mutableCopy, the compiler assumes that it returns an object with a +0 reference count, meaning it needs to be retained by the calling code if it is to be kept around, and doesn't need to be released if it's not being kept around. That's a long winded way of saying that it should neither leak nor cause a crash in your ARC code as written in your question.
Passing objects between ARC code and manual reference counting code doesn't require any special handling. You just need to make sure that methods' names match their memory management semantics in the non-ARC code. It certainly seems like you've done that in this case, although as you say, you didn't post your complete code.
Note: Presumably, objectWithString: returns an autoreleased object. If it doesn't it, it should (because it doesn't start with alloc, new, copy, mutableCopy).
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 have been puzzling over this for days now. I'm still trying to wrap my head around memory management in objective-c.
Here is my snippet (condensed for clarity):
- (void)performOperation:(NSString *)operation
{
if ([#"+" isEqual:operation])
{
waitingOperation = operation;
}
else if ([#"C" isEqual:operation])
{
waitingOperation = nil;
}
}
waitingOperation is merely a local private NSString (no #property, no #synthesize, no getters/setters).
Shouldn't I be leaking memory when I assign waitingOperation to nil when it's currently pointing to an NSString on the heap? My call to this method is in an ios app that is passing NSString from UILabel display.text. I've been profiling this code with Instruments and I never see any leaks. I would really appreciate some clarity on this. Thanks in advance.
You haven't laid a claim of ownership on waitingOperation by calling retain, so you have no responsibility to release.
This may lay you open to problems at some point if the string is released elsewhere (by disposing of the UILabel for example), in which case you'll be left with a dangling pointer. But you aren't leaking anything here.
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.