Adrian Anttila
Adrian Anttila

Reputation: 2038

What is the correct way to declare a private member variable for an Objective-C class?

I having trouble getting a variable assignment to work in the following initializer:

- (id)initWithBaseURL:(NSString *)url  {
  self = [super initWithNibName:@"MyNibName" bundle:nil];
  if (self) {
    baseURL = [url copy];
  }

  return self;
}

I have verified that the url parameter is valid with appropriate content (created as a NSMutableString built via an NSXMLParser, then examined in the initWithBaseURL method in the debugger), but after assigning the result of the copy operation to baseURL, the baseURL variable is "out of scope" and remains invalid. Any attempts to access the baseURL variable in other methods of the class result in an EXC_BAD_ACCESS error.

I have declared baseURL in the .h file as follows, with no @property or @synthesize operations, since I'm trying to use it as a private member variable:

@interface SignInViewController : UIViewController {
  // other variables

  @private
  NSString *baseURL;
}

// Other @property delcarations, IBAction method declartions, and method declarations

@end

I've also attempted to use the technique described here, but that only causes the EXC_BAD_ACCESS error to occur in the initializer.

What am I doing that would cause the EXC_BAD_ACCESS errors to occur, and how do I fix it?

Upvotes: 0

Views: 487

Answers (2)

Costique
Costique

Reputation: 23722

Here's a couple of observations which may or may not be relevant.

  • The code you posted is absolutely OK from Obj-C's point of view.
  • Instead of [super init] you should really call UIViewController's designated initializer, initWithNibName:bundle:.
  • You named the method initWithBaseURL:, but it takes NSString as an argument. While that does match the declaration in @interface, make sure you don't expect baseURL to be an NSURL object somewhere else in your code.
  • What happens when you remove @private? I suspect nothing changes with regard to the exception you see.

Make sure you don't do this:

NSString *myURL = [NSString stringWithFormat:...]; // myURL is autoreleased
SignInViewController *controller = [[SignInViewController alloc] initWithBaseURL: myURL]; // retains myURL because it's immutable
[myURL release]; // does not crash because myURL has been *retained*
// baseURL is left with 0 retain count

You can verify that sending copy to an NSMutableString does produce another object, while sending copy to an immutable NSString is equivalent to retain, just because wasting memory on exact copies of immutable objects is inefficient. If baseURL were indeed a copy of myURL, the crash would happen when the autorelease pool was drained.

In other words, a mistake may be in one place, only to manifest itself in another. The above example is not too contrived.

Upvotes: 4

justin
justin

Reputation: 104728

you've declared it correctly. also, the program you have posted is correct.

corr:

as Costique caught: you should call through one of the superclass's designated initializers.

Upvotes: 3

Related Questions