RaiseMan Challenge solution uses deprecated method


#1

The Apple documentation for method [NSBundle loadNibNamed:owner:] indicates that it’s deprecated in OS X 10.8. They seem to indicate that the instance method [NSBundle loadNibNamed:owner:topLevelObjects:] should be used instead. When I try to use the code below however, I get exceptions when I close the first instance of the About window and try to open the second.

  • (IBAction)showAboutPanel:(id)sender
    {
    NSBundle *appBundle = [NSBundle mainBundle];
    [appBundle loadNibNamed:@“PanelAbout” owner:self topLevelObjects:nil];
    [[self panelAbout] makeKeyAndOrderFront:nil];
    }

The panel is declared as a property in AppController like so:

@property (strong) IBOutlet NSPanel *panelAbout;

Any help is appreciated. Thanks.


#2

The crash happens when Cocoa sends release to the aboutPanel pointer. This is a part of assignment of a strong reference: release the old value. This particular assignment happens when Cocoa is loading the nib again and re-assigning the outlet. By the way, Zombies (p. 98) are a great way to help diagnose problems like this:

The reason for this crash is hinted at by the documentation for -loadNibNamed:owner:topLevelObjects:. Namely that this method has different memory management characteristics than the old -loadNibNamed:owner:, and it does not retain top level objects automatically. (This is somewhat of a more advanced topic that is not covered in the book.) So with -loadNibNamed:owner: the NSPanel (our about panel) is retained when loading, but with the new method it is not.

To avoid leaking a window, there is a property on NSWindow, -setReleasedWhenClosed:. This can be toggled on the Attributes inspector for the window. By default it is on. If you turn it off, the crash is fixed because the NSWindow is no longer released upon being closed.

Adam


#3

Thanks Adam, that’s great information. I don’t know how anybody can learn Apple programming without a book like this.


#4

another quick note.

There’s a difference between ‘a depreciated method’ etc… or an updated one :slight_smile:

Example:

[NSString stringWithCString:<#(const char *)#> length:<#(NSUInteger)#>]

is depreciated

and the above example (in the OP’s post) has been updated :slight_smile:


#5

Hi, sorry to bother with an old topic on an old book.
In my implementation of this challenge I had to also retain the topLevelObjects array of the nib file, otherwise the panel would close by itself at any event happening.
I’ve come with this solution —which works— but I’d like to know if it is the proper one:

- (void)showAbout:(id)sender {
    if (!_about) {
        NSArray *topLevelObjects;
        [[NSBundle mainBundle] loadNibNamed:@"About" owner:self topLevelObjects:&topLevelObjects];
        self.aboutTopLevelObjects = topLevelObjects;
    }
    if (!self.about.isVisible) {
        [self.about makeKeyAndOrderFront:nil];
    }
}

_aboutTopLevelObjects property is declared as strong and nonatomic in the interface anonymous extension of the class AppController:

@interface AppController ()

@property (weak, nonatomic) IBOutlet NSPanel *about;
@property (strong, nonatomic) NSArray *aboutTopLevelObjects;

@end

Thanks for any given feedback.
Cheers.