Chad Edge
Chad Edge

Reputation: 15

Reusing NSString in the same .m file

I have a string (currently defined in my .h file) that I'd like to fill and reuse in my .m file.

Here's the setup:

  1. User clicks btnA in my interface
  2. btnA runs a method (buttonAClicked) that sets the NSString's value to "foo"
  3. USer clicks btnB in my interface
  4. btnB runs a method (buttonBClicked) that returns the value of the NSString (eg: "foo")
  5. User clicks btnA again and the method updates the NSString "a new value"...
  6. Profit!

Here's some code:

/* Modal_TestAppDelegate.h */
// in the @interface block //
@public
NSString *countOfMatches;
// in the main area of the .h //
@property (nonatomic, readwrite, reatain) NSString *countOfMatches;
/* Modal_TestAppDelegate.m */
@synthesize countOfMatches;
-(void)applicationDidFinishLaunching:(UIApplication *)application{
... other code ...
self.countOfMatches = [[NSString alloc] initWithFormat:@"0"];
}
-(void)updateButtonClicked:(id)sender{
countOfMatches = @"1";
NSLog(@"countOfMatches is now: %@",countOfMatches);
}
-(void)readButtonClicked:(id)sender{
NSLog(@"I wonder what countOfMatches is set to now? %@",countOfMatches); // CRASH!
}

the "readButtonCicked area is where I'm crashing - it looks like I can't read the countOfMatches string anymore.

Any ideas on how I can simply reuse a "variable" throughout a single class (if I'm calling the .m implementation a "class" correctly - this is my first attempt and I'm kinda ripping pages out of the several Xcode and iPhone SDK books I have).

Thanks!

Upvotes: 0

Views: 617

Answers (3)

God of Biscuits
God of Biscuits

Reputation: 1338

The property has a copy characteristic, so it's making a copy of whatever's assigned, which in turn has the characteristic of retain (the copy it makes has a retain count of 1), so it doesn't matter if it's a string-literal or not.

But here's a slightly off-topic issue: if you're going to be mutating the value at all, why not store it as an NSInteger or NSUInteger?

If you're getting an NSString from somewhere else, just use the NSString method -integerValue and store the number.

And then any time you needed countOfMatches as a string, use NSNumber to get the value into a string form on the fly. This will also have the added benefit of gaining you localized string values. Assuming that self.countOfMatches is now an NSInteger:

NSString* countOfMatchesStr = [[NSNumber numberWithInteger:self.countOfMatches] descriptionWithLocale:[NSLocale currentLocale]];

Any good Cocoa or Cocoa Touch application should be internationalizable from the start, whether or not you intend to make it available in languages other than the initial language you're writing it in.

That means doing the following, at minimum:

  • whenever you create a new Xcode project, select each xib file, get info on it, click the General tab and at the bottom of the pane, click the button Make File Localizable. It's better to do this before you import the project into version control because it moves the xib file from the project level into a subfolder called XXXX.lproj, where XXXX is the language code of the language you're working in.

  • Never put user-facing string-literals into your source code! Create a .strings file: create a new file. When the template window comes up, in the Mac section choose Resource, then .strings file. Name the file Localizable.strings and save it to your project. Then be sure to follow the steps in the last bullet point to actually make it localizable.

  • for all those string-literals that you DID put into your source code, wrap them in calls to NSLocalizedString(@"My Button Label", @"an optional comment, use empty string if no comment"); I'll leave it to you to read more about how to format the contents of .strings files.

Upvotes: 0

Martin Gordon
Martin Gordon

Reputation: 36389

Your problem is occurring when you set countOfMatches to the string literal @"1" in updateButtonClicked:

String literals are autoreleased, which means that when this pass of the run loop is complete, it will receive the -release message.

The run loop completes when your top-most method is finished, which means that your updateButtonClicked: method will work fine, but when it's complete, countOfMatches will be pointing to garbage memory.

If you push your read button after the update button, your application will try to find an object in the memory that countOfMatches is pointing to and will find garbage instead. This is why you're application is crashing.

Two possible solutions exists:

  1. Access the variable through the property, which will take care of memory management for you:

self.countOfMatches = @"1";

  1. Access the variable directly and retain the string literal:

countOfMatches = [@"1" retain];

Upvotes: 0

Shaggy Frog
Shaggy Frog

Reputation: 27601

You should set your NSString property to copy, not retain. (More here)

@property (nonatomic, readwrite, copy) NSString *countOfMatches;

You're also leaking memory on this line

self.countOfMatches = [[NSString alloc] initWithFormat:@"0"];

It could be

self.countOfMatches = [[[NSString alloc] initWithFormat:@"0"] autorelease];

or even better:

self.countOfMatches = [NSString stringWithFormat:@"0"];

or even best (and really, what it should be):

self.countOfMatches = @"0";

There's no sense using any of the "format" methods of NSString -- you're just setting it to a static string.

Upvotes: 4

Related Questions