0

So I'm a little confused here. I have a Cocoa app, in the appdelegate header I'm declaring a NSDrawer that I've connected in Interfacebuilder and whose contentView I'm setting programmatically depending on the context. The contentviews contain Buttons that are connected to various functions in the Appdelegate.

@property (strong) IBOutlet NSDrawer *theDrawer;

When my app starts app, and I inspect it in the Debugger "theDrawer" is not nil and correctly instantiated by the Interfacebuilder. In the

Now if the user clicks any button it turns out that references to [[NSApp delegate] theDrawer] will be ignored because theDrawer is nil. which doesn't make sense to me. I tried fix this by specifically setting the delegate when the app launches.

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
  [NSApp setDelegate:self];
}

I've checked that self.theDrawer is not nil at that point. But even after I set the delegate explicitly, any future calls to [[NSApp delegate] theDrawer] are nil.

How can I make sure to access variables on my App delegate? My understanding was that calls to NSapp delegate will return a unique instance of the app.

It seems that when a user clicks on a button that this creates a new thread and NSApp delegate will return nil for all variables.

Any help appreciated

  • You shouldn't be using the app delegate for this (that's not what the class is there for). Add a setter with a breakpoint for the drawer property, when is it called? – Wain Apr 25 '14 at 20:29
  • Where else should I put a variable that I need to access from everywhere in the app? The setter gets called directly after the start in the main thread – Michael Scherbaum Apr 25 '14 at 20:42
  • 1
    If it's a view type object, then declare the outlet in the associated window or window controller that loads the NIB. NSApplication and delegate exist to bridge application and system events, not as global state buckets. – CodaFi Apr 25 '14 at 20:56

1 Answers1

1

The application delegate is properly set in main in main.m which you'll find in the "Supporting Files" folder of the project. Generally it's embedded in NSApplicationMain() which I believe references NSMainNibFile in the plist and actually has the main nib own the app delegate object instance. However Apple's not totally clear on how all that magic works. Nonetheless you can't set the delegate in applicationDidFinishLaunching -- that's a delegate function!

But if you're using a NSMainNibFile and a non-document application, the app delegate is likely being set to an object in your main NIB... in that NIB, the "File's Owner" is also the application delegate class, and the other outlets are non-nil within [NSApp delegate] because it's the main NIB file's owner. From the NSMainNibFile you can create outlets in the App Delegate class, because it's the file's owner.

If you create a second NIB, there are issues accessing the delegate. You don't want to create a object, because that isn't the same object as [NSApp delegate]. You can use the Application placeholder object and bind with a keypath of "application.delegate." But you can't create outlets because the app delegate can't be the file's owner.

But that's irrelevant, because if you have a second NIB that you're creating outlets for, they belong in a custom viewcontroller or windowcontroller subclass that you've declared is that file's owner. Even if that second NIB is loaded immediately, well then the app delegate should instantiate a controller instance to load and own the NIB, if you want outlets, it must be a custom subclass.

Apple sort of breaks this pattern by making the app delegate class the owner of the main menu "window" in MainMenu.xib in non-document applications... but that is because the menu window's a bit special. Anything you do outside of MainMenu.xib is going to need a custom controller class to have outlets.

Additional discussion in this answer about app delegate instances.

Community
  • 1
  • 1
stevesliva
  • 5,351
  • 1
  • 16
  • 39
  • Hi @Stevesliva, thanks for your answer, my issue is that I'm doing all this in the MainMenu.xib. All I wanna do is open and close a drawer off the main window. I have separate viewcontrollers for the contents of that drawer, but the main drawer is created and managed in MainMenu.xib. From my understanding the controlling class (Main App delegate) should manage the behaviour of the drawer, create classes to show and hide the drawer. However, as explained above if I tty to call an instance of the Main app this drawer variable is nil and I don't understand why. – Michael Scherbaum Apr 26 '14 at 07:19
  • Ok, just read the question you referred to and that actually exactly matches my problem. So I'll mark you're answer as solution. Thanks – Michael Scherbaum Apr 26 '14 at 17:21
  • That answer's mine, too. For some reason this way of shooting yourself in the foot is not well documented, but in a few weeks of pimping my own answers about it, it's come up twice on stackoverflow. The S.O. standard response is a lecture about what belongs in the delegate, but the way XCode sets up MainMenu.xib muddles the issue substantially, and there is special handling that ensures the Delegate object there is the same as NSApp delegate. Try it anywhere else, and you cause frustration. You *should* avoid the delegate owning outlets, but knowing why they are nil is important, too. – stevesliva Apr 26 '14 at 19:41