Synthetix
Synthetix

Reputation: 2135

NSString copy or alloc? Do you feel lucky?

I have a thread that needs information from the GUI before starting. What I mistakenly tried to do at first was create pointers to the NSTextFields like so:

NSString *info = [gui_field stringValue];

//launch thread
[self performSelectorInBackground:@selector(myMethod:) withObject:info];

This caused problems when I tried to manipulate "info" from within the thread. I assume this is the case because technically, it was still pointing to the string representation of the NSTextField outside the thread.

This fixed the problem:

NSString *info = [[gui_field stringValue] copy];

I assume this made a copy (with its own memory space) that did not rely on the NSTextField at all. I also assume this should be thread-safe.

Is this the appropriate way to do this? I suppose I could have done this:

NSString *info = [[NSString alloc] initWithString:[gui_field stringValue]];

Are the two producing the same result? And do I have to explicitly call release on the string when using "copy" or is it autoreleased by default?

Update: or, perhaps I could just send a pointer to the thread, and copy the string with "autorelease," adding it to the thread's autorelease pool:

NSString *info = [gui_field stringValue];

//launch thread
[self performSelectorInBackground:@selector(myMethod:) withObject:info];

-(void)myMethod:(NSString*)info
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *copied_str = [[info copy] autorelease];

    //do stuff

    [pool drain];
    //copied_str should now be history
}

This way, I don't have to worry about explicitly releasing copied_str. It will be gone once the thread ends.

Upvotes: 2

Views: 413

Answers (2)

morningstar
morningstar

Reputation: 9162

NSString *info = [[gui_field stringValue] copy];
NSString *info = [[NSString alloc] initWithString:[gui_field stringValue]];

Those do pretty much the exact same thing.

[self performSelectorInBackground:@selector(myMethod:) withObject:info];

-(void)myMethod:(NSString*)info
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *copied_str = [[info copy] autorelease];

No, you can't do that. Even accessing the GUI string object just to copy it could be enough for a crash.

I think this is a case where the usually recommended patterns for memory management really don't provide a great solution, so you can go outside them.

NSString *info = [[gui_field stringValue] copy];

//launch thread
//pass ownership to callee
[self performSelectorInBackground:@selector(myMethod:) withObject:info];

// myMethod owns info!
-(void)myMethod:(NSString*)info
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [info autorelease];

Another option is retain infoCopy as an instance variable somewhere. That would let you use normal memory management patterns, but it doesn't fit semantically. It's really not an instance variable, it's an argument.

"I asked for an argument."

"This is abuse."

Upvotes: 2

bryanmac
bryanmac

Reputation: 39306

No need to rely on luck :)

alloc, copy, new and mutableCopy mean you own the object. Both of those will give you a retained object. If you're managing memory, you need to release it.

By convention, other methods will give you an autoreleased object.

As an example, if you want an autoreleased object, you can call:

NSString *str = [NSString stringWithString:yourString];

See the memory management guide:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html

Specifically the four rules here:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html

You own any object you create

You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).

Finally, both will copy the string.

from the NSString docs:

initWithString: Returns an NSString object initialized by copying the characters from another given string.

copy is from NSObject. It defines copy as:

Return Value The object returned by the NSCopying protocol method copyWithZone:, where the zone is nil.

NSString implements the NSCopying protocol so copy will return a copy of the string.

There is one exception where the string isn't copied by initWithString - if you pass a string literal it will wrap the pointer to the constant and ignore retain/release. See here if you're curious: Difference between NSString literals

Upvotes: 4

Related Questions