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.
Related
Ok, I am an experienced C++ developer. I am learning Objective-C on the fly while trying to build a fairly substantial Cocoa application. I have done some simpler apps with Cocoa while gearing up for this project and I think I have a good handle on most of the concepts.
The memory management paradigm is still a little vague to me however so I build with the memory analyzer to help me find issues right away, and to be honest it has been awesome and has done more to help me understand Objective-C memory management that any of the documentation.
So here is my question.
I have two threads that communicate with each other using the performSelector:onThread:withObject:waitUntilDone: method. They pass objects between each other passed in as the withObject: parameter of the method.
Sample code is below:
- (BOOL)postMessage:(id <MessageTarget>)sender messageId:(NSInteger)msgId messageData:(void*)data
{
// message is allocated and retained here. retain count will be +2
Message* message = [[[Message alloc] initWithSender:sender messageId:msgId messageData:data] retain];
if(message)
{
// message will be released in other thread.
[self performSelectorOnMainThread:#selector(messageHandler:) withObject:message waitUntilDone:NO];
// message is released here and retain count will be +1 or 0 depending on thread ordering
[message release];
return YES;
}
return NO;
}
- (BOOL)sendMessage:(id <MessageTarget>)sender messageId:(NSInteger)msgId messageData:(void*)data messageResult:(void**)result
{
// message is allocated and retained here. retain count will be +2
Message* message = [[[Message alloc] initWithSender:sender messageId:msgId messageData:data] retain];
if(message)
{
// message will be released in other thread. retain count will be +1 on return
[self performSelectorOnMainThread:#selector(messageHandler:) withObject:message waitUntilDone:YES];
if(result)
*result = [message result];
// message is released here and retain count will be 0 triggering deallocation
[message release];
return YES;
}
return NO;
}
- (void)messageHandler:(Message*)message
{
// message will have a retain count of +1 or +2 in here based on thread execution order
if(message)
{
switch ([message messageId])
{
case
...
break;
default:
...
break;
}
// message is released here bringing retain count to +1 or 0 depending on thread execution ordering
[message release];
}
}
The analyzer is complaining about a possible leak of the Message object allocated in postMessage: and sendMessage: but the object is released in messageHandler:. The code runs correctly and does not leak, and I suspect the analyzer is simply not able to see that the Message object is being released in a different thread.
Now in case you are wondering why I do the second retain in the post/send methods and not in the messageHandler: method, it is because the postMessage: method is meant to be asynchronous and [message release] in the post method may get executed before the [message retain] in messageHandler: would, leaving me with an invalid object. It would work just fine if I did that in the case of the sendMessage: method because it is synchronous.
So is there a better way to do this that would satisfy the memory analyzer? Or maybe a way to give the memory analyzer a hint that the object is in fact being released?
Update:
Torrey provided the answer below but I wanted to clarify what I had to do different from what he suggested.
He suggested using an attribute on my messageHandler: method as below
- (void)messageHandler:(Message*) __attribute__((ns_consumed)) message;
This did not quite work since the object is being passed into performSelector: and the analyzer does not see it being passed along to messageHandler:
Since the performSelector: call is defined by Cocoa and not by me I can not add the attribute to it.
The way around this is to wrap the call to performSelector: as follows:
- (void)myPerformSelector:(SEL)sel onThread:(NSThread*)thread withObject:(id) __attribute__((ns_consumed)) message waitUntilDone:(BOOL)wait;
{
[self performSelector:sel onThread:thread withObject:message waitUntilDone:wait];
}
Then you can call the wrapper function and the analyzer will see the attribute and not complain about an unbalanced retain/release pair.
In the end I did not like the extra indirection just to get rid of the warning so I used the preprocessor as explained in my comment below. But I could see situations where it could be useful to use this method.
You do not need to explicitly transfer reference count ops to the secondary thread when using this API.
a) Your caller holds a reference when waitUntilFinished is true,
b) and the implementation behind + [NSThread performSelector:… also does (see: - [NSRunLoop performSelector:target:argument:order:modes:]).
There is no need to pass reference count duties for the parameters (or self) across threads in this case.
It will either be performed immediately, or self and the parameter (it's objc-typed) will be retained while in the other thread's run loop queue (and self and the parameter are released after the object's performed the selector).
(don't use ref-op __attribute__s to shut it up)
You should be able to make the analyzer happy by judicious use of Clang's ns_consumed attribute. As you suggested, this gives the memory analyzer a hint that a release message will be sent to the parameter upon completion of the function call. You would use it like:
- (void)messageHandler:(Message*) __attribute__((ns_consumed)) message
There is more information on Cocoa memory management annotations in the Clang analyzer documentation. You may want to wrap the attribute setting in an NS_COSUMED macro for compatibility with other compilers as suggested on that page.
I have the following code
- (NSString *)stringByEscapingXMLEntities;
{
NSString *result;
#autoreleasepool {
result = [self stringByReplacingOccurrencesOfString:#"&" withString:#"&"];
result = [result stringByReplacingOccurrencesOfString:#"\"" withString:#"""];
// ... lot of -stringByReplacingOccurrencesOfString: calls
result = [result stringByReplacingOccurrencesOfString:#" " withString:#" "];
}
return result;
}
I ask myself now how would I transfer ownership result out of the method. Before ARC I would have retained result before exiting the autorelease block and returned it autoreleased at the end of the method.
Thanks!
There are two ways to do that:
Rename the method to something like copyStringByEscapingXMLEntities -- the copy indicates the transfer of ownership and ARC creates the code accordingly.
Append, in the header, NS_RETURNS_RETAINED to the method definition like this: - (NSString *)stringByEscapingXMLEntities NS_RETURNS_RETAINED.
EDIT: As 'iljawascoding' mentioned, the #autoreleasepool has no real need to be kept around -- except for optimization.
EDIT 2: And remember: ARC always does the right thing. All the things you tried (your comment) result in the very same correct program -- albeit with the lack of some optimization if result was defined as __strong.
Get rid of the autorelease altogether. According to Apple, ARC will automatically insert the necessary release after your code is done with the temporary instances stored in 'result'. There's nothing to gain by rolling your own autorelease pool here.
The code you have posted is correct. Any crash has a different cause.
Because result is a strong reference outside the autorelease scope, ARC is responsible for keeping it alive when exiting the pool, and it does. You don’t need to do anything special.
More specifically, ARC generates code equivalent to this:
void *_arp = objc_autoreleasePoolPush();
temp1 = objc_retainAutoreleasedReturnValue([self stringByReplacingOccurrencesOfString:#"&" withString:#"&"]);
temp2 = objc_retainAutoreleasedReturnValue([temp1 stringByReplacingOccurrencesOfString:#"\"" withString:#"""]);
objc_release(temp1);
result = objc_retainAutoreleasedReturnValue([temp2 stringByReplacingOccurrencesOfString:#" " withString:#" "]);
objc_release(temp2);
// result is not released here
objc_autoreleasePoolPop(_arp);
return objc_autoreleaseReturnValue(result); // Result is returned autoreleased, or handed off to a matching objc_retainAutoreleasedReturnValue() in the caller.
Note that the temporary variables are handled with objc_retainAutoreleasedReturnValue/objc_release pairs. Because of the runtime optimization implemented by objc_retainAutoreleasedReturnValue and objc_retainAutoreleasedReturnValue, this means that the temporary values will actually be released immediately without ever being put in an autorelease pool if -stringByReplacingOccurrencesOfString: is built with ARC. My guess would be that most of the system frameworks aren’t yet, though.
Why not use [[NSString alloc] initWithString:result] before the #autoreleasepool scope closes? But why the ARP in the first place?
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 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];
It doesn't care about this:
NSString* leaker()
{
return [[NSString alloc] init];
}
I thought it would have been smart enough to check if any code paths could call that function without releasing its return value (I wouldn't normally code this way, I'm just testing the analyzer).
It reports this as a leak:
NSString* leaker()
{
NSString* s = [[NSString alloc] init];
[s retain];
return s;
}
but NOT this:
NSString* leaker()
{
NSString* s = [[NSString alloc] init];
// [s retain];
return s;
}
which seems particularly weak to me. Does it only analyze within the local scope? If the tool can't pick up on things like this, how can I expect it to pick up on actual mistakes that I might make?
clang does not perform any inter-procedure analysis, at least not yet. Even if it did, it might not necessarily catch this "error"- the permutations of potential code paths tends to rise super exponentially, making it a practical impossibility.
clang works with a set of heuristics that work "most of the time". Thankfully, Cocoa memory management rules tend to be fairly uniform, so the heuristics work for most uses. The specific example that you've given isn't really covered by the memory management rules, but I think most people (which includes myself) would tend to classify your example as "You've documented via the API that the caller of leaker() is responsible for releaseing the returned object". This is essentially analogous to - (NSString *)init... style methods.
clang knows that methods that begin with init... return an 'unreleased' object, and it is the callers responsibility to ensure that it is properly released. This forms part of the core of the heuristics- it doesn't need whole program or inter-procedural analysis to do the bulk of reference count checking- if a local block of code gets an object via an init... method, that local block of code needs to make sure it is properly released. Naturally, if the local block of code, and the object in question, is part of an init... method itself, it is covered by the same "rule", so it gets an exception.
What you probably want is something like:
NSString* leaker() __attribute__((ns_returns_retained))
{
return [[NSString alloc] init];
}
This lets the analyzer know that leaker() returns a 'retained' object, that the caller is responsible for properly releasing it. Although I haven't tested this, I strongly suspect that the 'leak' would be detected at the point where leaker() is called, i.e.:
void test(void)
{
NSString *leaked = leaker();
// Previous line should be caught as a "leak" by clang.
}
This is one of the unfortunate limitations of any static analyzer, not just clang.