Useful Nib Category Methods

If you’re loading a nib in your code that has a File’s Owner other than NSWindowController (or a subclass), then you (hopefully!) know that you need to release every top level object in the nib. If you use + [NSBundle loadNibNamed:owner:] then you need to create outlets to each of the top level objects, and release them when File’s Owner gets released.

That works fine and dandy if you do it, but if you forget to release a newly-added top level object, or forget to add an outlet to it so that you can release, you’ll leak memory. (That’s bad.) Is there an easy solution? You bet.


Enter a handy set of category methods:

//
//  NSBundleAdditions.h
//  Araelium Edit
//
//  Created by Seth Willits on 6/14/07.
//  Copyright 2007 Araelium Group. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@interface NSBundle (NSBundleAraeliumAdditions)

+ (BOOL)loadNibNamed:(NSString *)nibName owner:(id)owner topLevelObjects:(NSArray **)topLevelObjects;
+ (BOOL)loadNibFile:(NSString *)nibFilePath owner:(id)owner topLevelObjects:(NSArray **)topLevelObjects;

+ (NSArray *)loadWithTopLevelObjectsNibNamed:(NSString *)nibName owner:(id)owner;
+ (NSArray *)loadWithTopLevelObjectsNibFile:(NSString *)nibFilePath owner:(id)owner;

@end

//
//  NSBundleAdditions.m
//  Araelium Edit
//
//  Created by Seth Willits on 6/14/07.
//  Copyright 2007 Araelium Group. All rights reserved.
//

#import "NSBundleAdditions.h"

@implementation NSBundle (NSBundleAraeliumAdditions)

+ (BOOL)loadNibNamed:(NSString *)nibName owner:(id)owner topLevelObjects:(NSArray **)topLevelObjects;
{
    NSNib * nib = [[[NSNib alloc] initWithNibNamed:nibName bundle:nil] autorelease];
    return (nib  && [nib instantiateNibWithOwner:owner topLevelObjects:topLevelObjects]);
}

+ (BOOL)loadNibFile:(NSString *)nibFilePath owner:(id)owner topLevelObjects:(NSArray **)topLevelObjects;
{
    NSNib * nib = [[[NSNib alloc] initWithContentsOfURL:[NSURL fileURLWithPath:nibFilePath]] autorelease];
    return (nib  && [nib instantiateNibWithOwner:owner topLevelObjects:topLevelObjects]);
}

+ (NSArray *)loadWithTopLevelObjectsNibNamed:(NSString *)nibName owner:(id)owner;
{
    NSNib * nib = [[[NSNib alloc] initWithNibNamed:nibName bundle:nil] autorelease];
    NSArray * topLevelObjects = nil;
    
    if (![nib instantiateNibWithOwner:owner topLevelObjects:&topLevelObjects]) {
        return nil;
    }
    
    [topLevelObjects makeObjectsPerformSelector:@selector(release)];
    return topLevelObjects;
}

+ (NSArray *)loadWithTopLevelObjectsNibFile:(NSString *)nibFilePath owner:(id)owner;
{
    NSNib * nib = [[[NSNib alloc] initWithContentsOfURL:[NSURL fileURLWithPath:nibFilePath]] autorelease];
    NSArray * topLevelObjects = nil;
    
    if (![nib instantiateNibWithOwner:owner topLevelObjects:&topLevelObjects]) {
        return nil;
    }
    
    [topLevelObjects makeObjectsPerformSelector:@selector(release)];
    return topLevelObjects;
}

@end

If you use either of the latter two methods (loadWithTopLevelObjectsNib...:owner:) then all you need to do is release the array returned by the method, which contains references to the top level objects in the nib. Fantastic! So an example….

@interface MyNibLoadingClass : NSObject
{
    NSArray * mTopLevelObjectsInNib
}

@end

@implementation MyNibLoadingClass

- (id)init;
{
    if (!(self = [super init])) {
        [self release];
        return nil;
    }
    
    if (!(mTopLevelObjectsInNib = [[NSBundle loadWithTopLevelObjectsNibNamed:@"MyCoolNib" owner:self] retain]) {
        NSLog(@"Ahhhh!! Could not load nib!");
        [self release];
        return nil;
    }
    
    return self;
}

- (void)dealloc;
{
    [mTopLevelObjectsInNib release];
    [super dealloc];
}

@end

All you have to do is release that array and presto! Much easier than adding outlets and remembering to release everything yourself.

No Comment

Comments are closed.