Possible Memory Leak with [NSString alloc] - objective-c

I write plugins for a cross-platform application. My code depends almost entirely on the application's API for interacting with the outside world, which means my experience with the OS-level APIs is limited. But I do have a small amount of OS-specific code to accomplish goals that the application's API does not provide for. On the Mac side I have a number of code snippets like this one that changes the title of a Window:
First a utility function:
NSString *GetNSStringFromUTF16 ( const utf16char * const str )
{
size_t len = 0;
while ( str[len] )
len++;
NSData* data = [[NSData alloc] initWithBytes:str length:sizeof(*str)*len];
NSString* retVal = [[NSString alloc] initWithData:data encoding:NSUTF16LittleEndianStringEncoding];
[data release];
return retVal;
}
Then later I set the window title like this:
[myWind setTitle:GetNSStringFromUTF16(newTitleStr)];
I was running XCode Analyze and it flagged a number of "possible memory leaks" from calls to my GetNSStringFromUTF16 function. But it did not flag this one. Is it a memory leak or not?

Don't think. Just follow the Golden Rule: if you said alloc, copy, or retain, you must say release. Did you say alloc? Yes! Then you must say release. You need to autorelease your retVal before you return it.
Other comments: Do not start a method name with a capital letter. And do not start your function name with get! That has an effect on what the analyzer expects the memory management to be.
One last comment: If at all possible use ARC instead of manual memory management.

Related

Xcode static analyser & memory leak

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.

How to transfer ownership out of an #autoreleasepool with ARC

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?

Objective-c memory management

As a new comer to Objective-c and its memory management technique, comparing two pieces below. (the original code was exacted from apple.com autorelease pools)
Questions:
1. Do the two pieces achieve the same result?
2. Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
3. Does the 2nd code below violate anything as far as best practices? Performance?
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
for (NSString *fileName in args) {
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
NSString *fileContents = [[[NSString alloc] initWithContentsOfFile:fileName
encoding:NSUTF8StringEncoding error:&error] autorelease];
/* Process the string, creating and autoreleasing more objects. */
[loopPool drain];
}
/* Do whatever cleanup is needed. */
[pool drain];
exit (EXIT_SUCCESS);
}
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
NSString *fileContents = [[NSString alloc] autorelease];
for (NSString *fileName in args)
{
// NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
fileContents = [fileContents initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
//[loopPool drain];
}
/* Do whatever cleanup is needed. */
[pool drain];
exit (EXIT_SUCCESS);
}
Do the two pieces achieve the same result?
No. It's bound to blow up and/or leak when fileContents = [fileContents ... is called the second time. Implementations assume their instance's memory is zeroed because that is guaranteed by the runtime.
Even if one initializer is proven to work without issue or leaks, many of them will not.
Don't try to 'renew' your objects like this - just please do it the normal way =)
The sequence (in a non-gc environment) should be: 1) alloc 2) init...(using designated initializer) 3) dealloc.
Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
No. In addition to what is outlined in #1, you should not assume the address returned from alloc will be the same returned from the initializer. The initializer may choose to destroy the result of alloc and create or return another allocation. In this case, you will introduce a reference count imbalance (effect: leak and/or zombie).
Does the 2nd code below violate anything as far as best practices?
NSString *fileContents = [[NSString alloc] autorelease]; is weird, very unconventional. The first time I read it, I missed the error because it is so conventional for init...] to be placed between alloc and autorelease.
Does the 2nd code below violate anything as far as Performance?
Performance may suffer by removing the autorelease pool from the inner loop. When large allocations and/or high iterations occur, it's best to create small localized pools, like the original.
To answer your questions:
1) Do the two pieces achieve the same result?
No, they don't, mainly because the second example is incorrect, on two accounts.
Firstly, NSString *fileContents = [[NSString alloc] autorelease] makes very little sense. This line essentially allocates room for a string, and without even initializing it (and increasing its initial retain count to 1), telling it to put itself onto an autorelease pool. This is a big no-no.
And second, fileContents = [fileContents initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error] makes just as little sense. You're initializing a new object using old memory that has not been released or reset in any way, which is just undefined behavior. As Sherm Pendley mentioned, you cannot allocate space once and simply reuse that memory without concern, and since you have no idea how NSString is defined internally, this can either simply leak memory or very simply blow up in your face. Another big no-no.
The rest of the code appears to be fine (it does depend on what you're doing in the 'process' step). As others have pointed out, +alloc and -init are almost always paired with each other, but not just because of convention, but because that this pairing is simply the right thing to do. You must +alloc memory before you can begin to use it, and you must -init to get that memory zeroed and in the right condition to run properly. Any other combination might not be fit for use.
2) Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
No, they definitely do not. The second one will leak large amounts of memory, and very well might not even run. The first once is definitely okay, but might not run efficiently due to the use of inner autorelease pools.
3) Does the 2nd code below violate anything as far as best practices? Performance?
Yes, it does, and in fact, even the first one might not run efficiently. Creating an inner autorelease pool might not be in your best interest, and might even hinder your program. The venerable Mike Ash did some testing with autorelease pools, and the results are a little surprising. It turns out that differently-sized autorelease pools will work better on different computers (back then, on his old computer, 100 objects was the most efficent per pool; for me, 10000 objects works a little bit better, and even then only marginally better than 1000 objects)

If I want to make a new instance of an object in a function whose pointer is passed by reference in it

- (void)createAString:(NSString **)str
{
*str = [NSString stringWithString:#"Hi all!"];
[*str autorelease]; // ???? is this right ?
}
How should I use release or autorelease ? I don't want to release outside of the function of course :)
...
NSString *createStr;
[self createAString:&createStr];
NSLog(#"%#", createStr);
You're correct that you'd generally want to return autoreleased (or the like) objects from out params when you use this form. Your assignment statement in the function that sets *str to a string:
*str = [NSString stringWithString:#"foo"];
is already doing the right thing, because that method returns an instance of NSString that the caller doesn't own. Just like you could return this string object from your function without any further memory management, you can set it as the outparam as you've done. Your second snippet showing the call site is fine.
This said, I'm worried about a few things in your code that you should be sure you understand:
The value of str inside the method is still a **, and sending that a message (as you've done for the speculative autorelease) is nonsense. Be sure you fully understand doubly indirected pointers before using them too liberally. :) If you need to send str a message after creating it, send it to *str, which is what contains the NSString *.
Setting an outparam like this when the function returns void is not idiomatic Cocoa. You would normally just return the NSString * directly. Outparams are rare in Cocoa. (Usually just NSErrors get this treatment from framework calls. Otherwise they conventionally use name like getString to differentiate them from normal get accessors which don't use the word "get".)
I hope -stringWithString was just an example. That method is almost never used in practice, since it's equivalent (in this case) to just using a #"string literal" (although that would muddy your example).
Instead of using a double pointer, would it not be more elegant to use an NSMutableString instead?
- (void)createAString:(NSMutableString *)str
{
[str setString:#"Hi all!"];
}
....
NSMutableString *createStr = [[NSMutableString alloc] init];
[self createAString: createStr];
NSLog(#"%#", createStr);
[createStr release];
Or, even better, just have the createAString method return an NSString.
- (NSString *)createAString
{
return #"Hi all!"; // this is autoreleased automatically
}
I wouldn't want to presume that your needs are this simple, though. =)

Is the scope of what Xcode's "Build and Analyze" will catch as a leak supposed to be this limited?

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.