NSMenuDelegate not called for submenu in NSMenuItem created by another NSMenuDelegate

1.3k views Asked by At

I have an NSStatusItem with an NSMenu that has a delegate. This delegate dynamically updates the menu based on a few factors. So per the documentation, to quickly update the menu, I use the method:

- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel;

At the appropriate time in this method I call the following:

NSMenu *submenu = [[NSMenu alloc] init];
SomeSubmenuDelegate *submenuDelegate = [[SomeSubmenuDelegate alloc] init];
submenu.delegate = submenuDelegate;
item.submenu = submenu;

The status menu shows correctly, and the appropriate menu items have disclosure triangles for submenus; however, the submenus do not appear when the appropriate items are highlighted, even though in the SomeSubmenuDelegate the following method exists, which based on my experience should show three blank menu items:

- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
{
    return 3;
}

Logging does nothing as well, not even in menuWillOpen, so it appears the methods are never called, making me believe that somewhere the delegate is unset, or the menu is copied somehow during runtime and the delegate unset in the process...

The workaround for now is to create the entire submenu in the top level NSMenuDelegate (for the NSStatusItem's menu) instead of assigning a delegate. There are a few drawbacks here

1) I am keeping my actions within the NSMenuDelegate, and it would be nice to have separate files for the parent and submenus.

2) While not necessarily material, that means that I am allocating memory for each submenu NSMenuItem before it is every needed.

It seems optimally, I should be able to use a delegate here just like with any other NSMenu.

Any thoughts / suggestions are greatly appreciated. First question for me here!

EDIT: Is this because of ARC? I may have incorrectly assumed that by convention ARC would retain the delegate object, thinking in terms of Garbage Collection, but maybe it is being released because delegates are weak references and ARC doesn't make assumptions like that. I could only find reference to garbage collection in the Apple docs, which does say that garbage collection would maintain a strong reference... but obviously, ARC is not garbage collection, and I can't find any information on this. This means I would need to retain the delegate in the parent delegate, which seems ugly for something so dynamic. Is there a way to retain the delegate under ARC and then release it at the appropriate time without a reference in the parent delegate (or anywhere else for that matter)?

1

There are 1 answers

0
ThorstenC On

In my case I moved the declaration of my menu instance from method to a member variable.

from

-(void)awakeFromNIb{
 ..
 [self setMenu:[[MyMenu alloc] init]];

to

 @implementation MyView{
   MyMenu *myMenu;
 }
 -(void)awakeFromNib{
   ...
  myMenu = [[MyMenu alloc]init];

  [self setMenu:[myMenu createMenuStructure]];

The method :'createMenuStructure' locally created my Menu. 'MyMenu' implements the NSMenuDelegate.

This holds the myMenu variable as a strong reference. The handler now works perfectly.