About | ACP | Buy | Forum | Industry Watch | Learning Curve | Search | Twitter | Xnews
Home » Learning Curve » Developers Workshop

OO's a Tool - Not a Fetish

You use what's right in any one given situation - right for the customer that is.


Buy It

Try It

A well known programmer with a well known third party product for OS X likes to derive classes. He's always looking to make things easier for himself. One of the things he looked closer at was the NSBundle method pathForResource:ofType:.

pathForResource:ofType:
Returns the full pathname for the resource identified by a given name and specified file extension.

(NSString *)pathForResource:(NSString *)name ofType:(NSString *)extension

Discussion
If extension is an empty string or nil, the returned pathname is the first one encountered where the file name exactly matches name.

The method first looks for a matching resource file in the nonlocalized resource directory (typically Resources) of the specified bundle. If a matching resource file is not found, it then looks in the top level of any available language-specific '.lproj' directories. (The search order for the language-specific directories corresponds to the user's preferences.) It does not recurse through other subdirectories at any of these locations. For more details see Bundles and Localization.

The following code fragment gets the path to a localized sound, creates an NSSound instance from it, and plays the sound.

NSString *soundPath;
NSSound *thisSound;
NSBundle *thisBundle = [NSBundle bundleForClass:[self class]];

if (soundPath = [thisBundle pathForResource:@"Hello" ofType:@"snd"])  {
    thisSound = [[[NSSound alloc] initFromSoundfile:soundPath] autorelease];
    [thisSound play];
}

Which looks good in and of itself but it does require splitting up the filename from @"Hello.snd" into "@Hello" and @"snd".

Too much work.

So our friend decided to subclass NSBundle instead.

@interface MyBundle : NSBundle {}
-(NSString *)pathForResource:(NSString *)path;
@end

@interface MyBundle : NSBundle
-(NSString *)pathForResource:(NSString *)path {
    NSString *type = [path pathExtension];
    NSString *name = [path stringByDeletingPathExtension];

    return [super pathForResource:name ofType:type];
}
@end

Which again appears all and good and in fact can be tweaked advantageously into the following.

@interface MyBundle : NSBundle {}
-(NSString *)pathForResource:(NSString *)path;
@end

@interface MyBundle : NSBundle
-(NSString *)pathForResource:(NSString *)path {
    return [super pathForResource:[path stringByDeletingPathExtension] ofType:[path pathExtension]];
}
@end

But the snag is that it doesn't help a bit and only hurts. Most of these paths used in applications are 'static': they're character constants embedded into the executable. They're not variables and -[MyBundle pathForResource:] isn't intended to work that way. It's only a convenience - to the developer.

So let's tally up. On the one side we have two NSString 'constants' embedded into the executable; on the other we have one. The difference in storage is mostly down to the byte alignment and is negligible.

On the one hand we simply call a framework method; on the other we have the overhead of a new class, a method within that class, and an additional two framework method calls within that method.

And we still have to call the original NSBundle method.

So guess who wins?

See Also
Software Reviews: The Very Ugly

About | ACP | Buy | Forum | Industry Watch | Learning Curve | Search | Twitter | Xnews
Copyright © Rixstep. All rights reserved.