1

In a pet application I'm writing, on the main window, I have a few custom views aligned under each other, each with a label, a combobox and a button.

Clicking the button invokes code that finds the combobox in the same view, and then calls the following function (a class method of RVListEditorController):

+ (void) editComboBox: (NSComboBox *) aComboBox
{
    // Analyze says "possible leak", but both methods ending the panel will release the controller.

    RVListEditorController *controller = [[RVListEditorController alloc] initWithComboBox: aComboBox];

    [NSApp beginSheet: [controller window]
       modalForWindow: [aComboBox window]
        modalDelegate: controller
       didEndSelector: NULL
          contextInfo: nil];
}

The code creates an instance of RVListEditorController. That controls a panel that allows me to edit the list in the combobox (remove items, sort items, etc.). It has, among other controls, two buttons that close it, Cancel and OK.

The code for the two buttons is:

- (IBAction) closeSheetWithOK: (id) sender
{
    [NSApp endSheet: editingPanel];
    [editingPanel orderOut: self];
    [comboBoxValues setArray: valuesCopy];
    if (comboBoxValues.count > 0)
        [editedComboBox setStringValue: [comboBoxValues objectAtIndex: 0]];
    [self release];
}


- (IBAction) closeSheetWithCancel: (id) sender
{
    [NSApp endSheet: editingPanel];
    [editingPanel orderOut: self];
    [self release];
}

These are the only two buttons that close the sheet. My question is about the lifetime management of the instance. It is allocated in the class method, but then control is handed to Cocoa again and the class method ends. The only place I could find to release the instance is in the two handlers for the closing buttons. My problem is that beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo: doesn't simply open the sheet and then waits until it closes again, returning a value how it was closed. If that were the case, I could close the instance in the class method, and I would feel better.

My question: Is there perhaps a better way to handle the lifetime of the instance, or is there something in Cocoa that allows me to open a sheet window-modally and then wait for it to close again, so I could release the instance right after that? I can't think of any, but I am a relative newbie, after all.

FWIW, the code works, so there are no errors. I am simply not very happy with the construct that I allocate something in a class method that must then be released in two instance methods of itself.

Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94

3 Answers3

2

That looks to me like something which should not be a class method and the problems you are having defining its lifecycle are a warning sign that it is being created without clear ownership.

Jonah
  • 17,918
  • 1
  • 43
  • 70
  • Well, I had that structure in place, and first wanted to open a modal window, which would have made my cycle of `create-instance` `use-instance` `release-instance` possible. But with sheets, this is a little different. Letting a class method take care of all the editing (no matter how) relieves the caller of handling (or knowing about) anything regarding the window, and only call editComboBox:. I'm not sure how making it an instance method (of what?) could improve the situation. – Rudy Velthuis Jul 22 '11 at 19:30
  • FWIW, would providing a method to release the window in the delegate be a possibility? I could pass the selector to beginSheet:etc:etc: and let that handle it. – Rudy Velthuis Jul 22 '11 at 19:34
  • And how would you do it? That it is a problem is clear to me, but not how making it an instance method (of what?) would solve this. The problem seems to me that beginSheet: does not open the window, waits and returns when it is closed again. That would keep the logic very simple. – Rudy Velthuis Jul 22 '11 at 19:42
  • Well given that the sheet is presented in a window I would be tempted to make whatever controller is responsible for that window also be or be the owner of the delegate of action sheets presented in that window. Hard to say without knowing anything about the structure of your app though. It also seems odd that a view (NSComboBox) seems to be central to this application logic instead of leaving it all in the controller layer. – Jonah Jul 22 '11 at 21:14
  • The NSComboBox is not central to this app, but it is what a RVListButtonController is controlling, and that opens an RVListEditor to do the editing. The ListButtonController had no info on the RVListEditorController except that it can tell it to edit the contents of a combobox. I found a combobox list too trivial to give it a model of its own, so the combobox simply manages its own contents. I might change that later. – Rudy Velthuis Jul 22 '11 at 21:51
1

I am simply not very happy with the construct that I allocate something in a class method that must then be released in two instance methods of itself.

There is a certain logic to that - but I would also claim that a window-modal sheet would more naturally be initiated by an instance method. The window is after all a representation of some object, not just a class.

That didn't answer your more general question about life cycles, though.

Monolo
  • 18,205
  • 17
  • 69
  • 103
  • It also doesn't answer how I could improve this. I don't see how doing this from a class instance would take away the problem, which seems to be that beginSheet:etc. returns immediately and simply opens the sheet, leaving the release of the sheet to other code. If it waited until the sheet was closed, I could do the release in the same method, and then it would be totally irrelevant if it was a class or instance method. – Rudy Velthuis Jul 22 '11 at 20:41
  • 1
    I don't think there is much of a way to improve this. What I do is that I keep the two methods as symmetric as possible and physically close to each other in the source file (one above the other, literally). The very robust retain count mechanism of the runtime will take care of the rest. You only "leak" if the user never closes the sheet, and that hardly counts as a leak. – Monolo Jul 23 '11 at 08:02
  • Monolo: Finally someone who understands the problem. Agreed, there is no real solution. But I found something that comes close. See my own answer to this question. At least you understand the problem. – Rudy Velthuis Jul 23 '11 at 08:18
1

I managed to get something that worked to my satisfaction. I provided beginSheet: with a method to be called after the sheet ended, giving controller as the context info. IOW:

    [NSApp beginSheet: [controller window]
       modalForWindow: [aComboBox window]
        modalDelegate: controller
       didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo)
          contextInfo: (void *)controller];
}

The code for the two buttons is now:

- (IBAction) closeSheetWithOK: (id) sender
{
    [comboBoxValues setArray: valuesCopy];
    if (comboBoxValues.count > 0)
        [editedComboBox setStringValue: [comboBoxValues objectAtIndex: 0]];
    [NSApp endSheet: editingPanel];
}


- (IBAction) closeSheetWithCancel: (id) sender
{
    [NSApp endSheet: editingPanel];
}

and the code for sheetDidEnd:returnCode:contextInfo: is:

- (void) sheetDidEnd: (NSWindow *) sheet returnCode: (NSInteger) returnCode contextInfo: (void *) contextInfo
{
    [sheet orderOut: (id)contextInfo];
    [(id)contextInfo release];
}

That is, IMO, the best that can be done for situations like this. The procedure would have been the same if this had been called from an instance method of the window controller, AFAICT.

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94