views:

106

answers:

6

If I declare a function such as:

NSString* createAString(std::string toConvert);

NSString* createAString(std::string toConvert)
{
      return [NSString stringWithUTF8String:toConvert.c_str()];
}

I was under the impression that because I didn't call alloc on the string it will be in the autorelease scope.

When I run this code XCodes memory leak detector is telling me there is a memory leak from this point. Can I not mix C style functions and Objective C types in this way or is there more of a fundamental problem at hand?

Cheers Stubear

A: 

It should be:

// Declaration (.h)
- (NSString *) createAString(std::string);

// Usage (.m)
- (NSString *) createAString(std::string) {
    return [NSString stringWithUTF8String:toConvert.c_str()];
}

What's the std::string for?

Emil
Wouldn't it then need to be part of an objective C class?Sorry I didn't quite explain it perfectly clear in the initial question but I wanted the function to not be part of a class, just a set of helper functions.As of now to quickly fix the problem I just created a a class called StringManipulation to handle this kinda problems and just made the functions static but again this wasn't really want I wanted due to the fact I wanted to neaten up code by just been able to write createAString("happyTimes"); instead of [StringManipulation CreateAString:"badTimes"];
stubear
If you're going to be passing literal strings into this function (such as `createAString("happyTimes");`), why not just use the built-in syntax for literal NSStrings: `@"happyTimes";`
Justin Voss
A: 

The string you return will be autoreleased, but what about the (C++) string that you pass in to the method? Do you free that afterwards?

The other thing that occurs to me is that LLVM, the compiler that is used to perform the static analysis, does not support C++ yet (at least not in an publicly released version). It could simply be a false-positive brought about because it doesn't fully understand some of the code.

Stephen Darlington
`std::string` is an object on stack, so its constructor and destructor is called automatically. He didn't use `std::string*`. It's not like Obj-C ...
Yuji
Just read your answer. I've not tested it but it makes sense.
Stephen Darlington
+2  A: 

There's nothing wrong with your code. You can mix C-style functions in Objective-C codes. I don't see any problem with the retain/release of Obj-C objects, nor new/delete of C++ objects.

But the name of your function violates the Create Rule. i.e. if the name of a function or a method contains alloc, create or copy, it is assumed to return an NSObject or a CF object with retain count 1. The XCode static analyzer works assuming this rule. You also work with this rule in mind. Otherwise the retain/release would be messed up.

Try Build&analyze this file.

#import <CoreFoundation/CoreFoundation.h>

extern CFStringRef FooCreate(void);
int main (int argc, const char * argv[]) {
    CFStringRef string=FooCreate();
    /* CFRelease(string); */
    return 0;
}

You can see the result of the analyzer changes if you (un)comment CFRelease. You don't have to provide the definition of FooCreate. Even if you do provide, the current analyzer doesn't look into it, instead it relies on the function name.

Your code returns an autoreleased variable even though the method name has create in it. That might have confused the analyzer to give a false positive.

Yuji
The analyzer certainly does not look at function names to detect leaks!
Andrew Grant
Wha?? Of course it does. I'm not talking about Leaks tool, I'm talking about static analyzer. I added an example in the answer; please try that yourself with the latest analyzer.
Yuji
+1 except that the "Create" rule only applies to functions, *not* methods.
Dave DeLong
Aah, yes indeed. I didn't know that. Is there any Cocoa method whose selector `create` in it? That would be utterly confusing... I need to re-check my code.
Yuji
@Yuji `+[UIPasteboard pasteboardWithName:create:]` is the only one I found.
Dave DeLong
I see. Now that you pointed it out, I found a couple of others, like `-[NSFileManager createFileAtPath:contents:attributes:]`. Well this doesn't return an object, so it's not an issue.
Yuji
A: 

This code looks fine to me.

It's likely that elsewhere you have some code that incorrectly retains this string and does not later release it, resulting in a leak. XCode cannot tell you where there is a mismatched retain/release, it can only tell you where the leaked object originated.

Also as others have mentioned, your use of "create" violates the naming rules of Objective-C. You should rename this to stringFromStdString or something similar. For bonus points, you could make it an extension of the NSString class.

Andrew Grant
See my reply above; the static analyzer does check the function name.
Yuji
A: 

Check if you're not calling this from outside the pool (stringWithUTF8String: uses it). Try wrapping this in

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    //...
[pool drain];

If it solves the problem, then you're (like you said) from outside the scope.

CaesarT
+2  A: 

@Yuji's answer is dead on and the correct answer.

I just wanted to point out that instead of making this a C function, you could make it "more Cocoa" and use a category:

//NSString+STDConversion.h
@interface NSString (STDConversion)

+ (NSString *) stringWithStdString:(std::string toConvert);

@end

//NSString+STDConversion.mm (note the .mm extension)
@implementation NSString (STDConversion)

+ (NSString *) stringWithStdString:(std::string toConvert) {
  return [NSString stringWithUTF8String:toConvert.c_str()];
}

@end

Now elsewhere you can do:

std::string myString = "This is my string";
NSString * myCocoaString = [NSString stringWithStdString:myString];
Dave DeLong