3
votes

I have a very basic initialization problem with an OS X app I am writing. I am initializing instance variables in initWithNibName in my NSViewController. However, these variables lose their value and end up being equal to nil by the time I access them in my IBAction methods.

Here is my code. I have an application that has 1 main window, and 1 NSViewController. The app delegate is programatically calling initWithNibName in applicationDidFinishLoading. The view appears, and the button in the view successfully calls onButtonClick. However, in that method, the value of myString is null. The logs clearly show that it was successfully initialized during the initWithNibName method. So it lost its value between initWithNibName and the button click.

//MyAppDelegate.m
@implementation MyAppDelegate

@synthesize window; 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    MainView* mainViewController = [[MainView alloc] initWithNibName:@"MainView" bundle:[NSBundle mainBundle]];
    [mainViewController loadView];
NSView *controllerView = [mainViewController view];
    [window setContentView:controllerView];
}

@end

//MainView.h
@interface MainView : NSViewController {
    NSButton *myButton;
    NSString* myString;
}

@property (assign) IBOutlet NSButton *myButton;
@property (assign) NSString* myString;

- (IBAction)onMyButtonClick:(id)sender;

@end

//MainView.m
@implementation MainView

@synthesize myButton;
@synthesize myString;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    NSLog(@"initWithNibName");
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Initialization code here.
        NSLog(@"Initializing...");
        [self setMyString:@"try to set the variable"];
        NSLog(@"String is %@", myString);
    }

    return self;
}

- (void)awakeFromNib {
    NSLog(@"awakeFromNib");
    //[self setMyString:@"init in here works, but is called too many times"];
}

- (IBAction)onMyButtonClick:(id)sender{
    NSLog(@"Button clicked");
    NSLog(@"%@", myString);  //Always nil when initialized in initWithNibName()!!
}

@end


//Logs

initWithNibName
Initializing...
String is try to set the variable
awakeFromNib
awakeFromNib
Button clicked
(null)

How can I get myString to retain its value?

I have tried every type of property declaration I can think of. I have also tried various methods of assignment ([self setMyString], self.myString = @"blah", myString = [NSString stringWithFormat:@"blah"], etc).

I have read that I should do my initialization in awakeFromNib(). This actually does work. However, my eventual initialization code will include a series of [[MyObj alloc] init] calls, and when they get called multiple times, multiple pointers result and it's madness from there. My end goal is to have one pointer to an object, which the controller can always refer to and always get the same data. The object gets initialized by the controller, which holds the reference and updates it as needed as the user interacts with the controller.

So is there any way to initialize instance variables in NSViewControllers, only once, and have them retain their value for the life of the controller?

Thanks

Kev

3
I've copied your code into a test project and it works fine. You have something set up wrong, probably in the NIB. Try creating a fresh project and using the stock MainView.xib that's automatically generated by Xcode.Francis McGrew
Is there any way you can send me a zip file of your test project so I can compare the two? Thanks.Kevin
Thank you! Thank you! Thank you! I figured out what I was doing wrong. I was adding a view controller to each of my nibs that represented the controller for the view. Then I was attaching the button clicks to that separate view controller object. Your project showed me that I should be attaching the button clicks to the File Owner, then setting the file owner to be the view controller. There is basically no need to import a second view controller into the nib when you can just use file owner as a placeholder instead. Thanks again!Kevin

3 Answers

1
votes

You should declare the property as (copy) instead of (assign)

0
votes

Have you tried declaring them as (nonatomic, retain)?

0
votes

Allow me to answer this since the correct answer is buried in the comments in my question.

I figured out what I was doing wrong. I was adding a view controller to each of my nibs that represented the controller for the view. Then I was attaching the button clicks to that separate view controller object. @Francis-McGrew attached a project that showed me that I should be attaching the button clicks to the File Owner, then setting the file owner to be the view controller. There is basically no need to import a second view controller into the nib when you can just use file owner as a placeholder instead.