Martyn
Martyn

Reputation: 30

Unable to access App Delegate property

I'm trying to access a property in my app delegate from another class (something I thought would be rather simply) but I'm having troubles in doing so. My files currently look like this:

LTAppDelegate.h

#import <Cocoa/Cocoa.h>
#import "Subject.h"

@interface LTAppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate> {

}

@property Subject *selectedSubject;

@end

LTAppDelegate.m

@synthesize selectedSubject;

The value for selectedSubject is then set inside applicationDidFinishLaunchingin LTAppDelegate.m. Now I'm wanting to get access to this from another class that I have, which is called LTTableViewController and is setup like so:

LTTableViewController.h

#import <Foundation/Foundation.h>
#import "LTAppDelegate.h"
#import "Subject.h"
#import "Note.h"

@interface LTTableViewController : NSObject{
    NSMutableArray *notesArray;
    LTAppDelegate *appDelegate;
    Subject *s;
}

-(IBAction)currentSubjectDetails:(id)sender;

@end

LTTableViewController.m

#import "LTTableViewController.h"

@implementation LTTableViewController

- (id)init
{
    self = [super init];
    if (self) {
        appDelegate = ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]);

        s = [appDelegate selectedSubject];
        NSLog(@"Test Subject: %@", [s title]);

    }
    return self;
}

-(IBAction)currentSubjectDetails:(id)sender{
    NSLog(@"Selected Subject: %@", [s title]);

}

After inserting various NSLog() messages it would appear that the init method of LTTableViewController is called before applicationDidFinishLaunchingis called in LTAppDelegate. Based on that it makes sense that the "Test Subject" NSLog() in LTTableViewController.m init displays null; however, the 'currentSubjectDetails' method is linked to a button on the interface and when that is pressed after the app is finished loading, the NSLog() message still returns null.

Is there anything obvious I'm missing here. I feel like I'm being a little stupid and missing something really basic.

Upvotes: 0

Views: 1011

Answers (3)

Igor Bulgakov
Igor Bulgakov

Reputation: 183

-applicationDidFinishLaunching called when e.g. all nib's object initialized, so launching will be ended after construction of views related stuff. This means that constructors of nib's objects wouldn't use any other nib's objects (your delegate and controller initializing with nib, right?).

Try to use -awakeFromNib instead of constructors, I think it will called after construction of both objects.

If you are trying to avoid often calls of ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]) I'll recommend to pass it as method parameter, in function stack. Cyclic references defense and some flexibility.

Upvotes: 1

Vervious
Vervious

Reputation: 5569

Well, s does not exist, since it is set to null in init, so -currentSubjectDetails prints null. It is not a good idea to set your private variables in the constructor if they depend on other objects.

Rather, let the other objects explicitly tell your controller that it should use that Subject (e.g., treat s as a property).

Or, just query ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]); every time.

Upvotes: 1

allprog
allprog

Reputation: 16790

Similar issue is described here http://iphonedevsdk.com/forum/iphone-sdk-development/11537-viewcontroller-called-before-applicationdidfinishlaunching.html Adding this kind of functionality in the constructor is usually not recommended. Generally, I'd suggest using parameters and not relying on hidden dependencies as those will necessarily depend on the order of execution and you lose the help of the compiler to avoid invalid values. View controller initializers should not be used to store mutable references since view controllers are initialized automatically by predefined constructors, and you cannot pass parameters to them this way.

If you need to access the app delegate, then obtain it, perform operations on it and drop the reference. Try not to cache it, you'll very likely introduce hidden issues. I suggest you hook into the appear-disappear cycle if the viewed contents depend on any kind of current state.

Upvotes: 2

Related Questions