2

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.

Synthetix
  • 2,035
  • 3
  • 24
  • 30

2 Answers2

4

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

Community
  • 1
  • 1
bryanmac
  • 38,941
  • 11
  • 91
  • 99
  • Thanks. See my update. Perhaps it would be better to use copy with autorelease and do this all within the thread. What do you think? – Synthetix Nov 16 '11 at 02:34
  • you can either copy with autorelease or call stringWithString. If you're not going to use it outside the scope of that function then getting an autoreleased one is less code and it will be freed in the near future after that function returns. – bryanmac Nov 16 '11 at 02:35
2
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."

Community
  • 1
  • 1
morningstar
  • 8,952
  • 6
  • 31
  • 42