views:

567

answers:

5

I was trying to create a static variable to store a dictionary of images. Unfortunately, the best way I could find to initialise it was to check in each function that used the variable. Since I am creating this variable inside a category, I can't just initialise it inside the initialiser. Is there a neater way of initialising navigationBarImages?

static NSMutableDictionary *navigationBarImages = NULL;

@implementation UINavigationBar(CustomImage)
//Overrider to draw a custom image
- (void)drawRect:(CGRect)rect
{
    if(navigationBarImages==NULL){
        navigationBarImages=[[NSMutableDictionary alloc] init];
    }
    NSString *imageName=[navigationBarImages objectForKey:self];
    if (imageName==nil) {
        imageName=@"header_bg.png";
    }
    UIImage *image = [UIImage imageNamed: imageName];
    [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}

//Allow the setting of an image for the navigation bar
- (void)setImage:(UIImage*)image
{
    if(navigationBarImages==NULL){
        navigationBarImages=[[NSMutableDictionary alloc] init];
    }
    [navigationBarImages setObject:image forKey:self];
}
@end
+1  A: 

One option is to use C++. Change the file's extension to .mm and replace = NULL with [[NSMutableDictionary alloc] init].

Marcelo Cantos
Will that work on the iPhone?
Casebash
+2  A: 

All you need is to set your static once at a known point before it is used. For example, you can set an NSApplication delegate and have it do the work in -applicationDidFinishLaunching:

Jon Reid
I can't access it in another file - static variables are only declared within the scope of a class
Casebash
Just define a standalone setup function.
Jon Reid
Nope. That won't work. *Something* has to call the setup function.
bbum
But that's what I meant, Bill. Provide a setup function, advertise it in a header file, and call it from `-applicationDidFinishLaunching:`
Jon Reid
(By "standalone," I meant "plain ol' C function.")
Jon Reid
+6  A: 

Consider this approach,

static NSMutableDictionary *navigationBarImages()
{
    static NSMutableDictionary *dict = NULL;
    if(dict == NULL)
    {
        dict = [[NSMutableDictionary alloc] init];
    }
    return [[dict retain] autorelease];
}

then whenever you woulde use navigationBarImages, replace it with navigationBarImages(), like this:

change

NSString *imageName=[navigationBarImages objectForKey:self];

to

NSString *imageName=[navigationBarImages() objectForKey:self];

If the function call overhead bothers you, maybe use a temporary variable to catch the return of navigationBarImages(),

NSMutableDictionary *dict = navigationBarImages();
[dict doSomething];
[dict doSomething];

The drawback is once you called navigationBarImages(), the instance of NSMutableDictionary got created, then it'll never get chance to dealloc until the end of the program.

yehnan
This lifetime is typical for static variables though, so I wouldn't really consider it a drawback per se.
Quinn Taylor
A: 

You could add +initialize in the .m file of your category — you'll just need to make sure you're not smashing an existing implementation or you'll get general wonkiness. (Obviously, you can be sure of this if you wrote the code, but with third-party code, this is probably not the best approach.)

Quinn Taylor
...and how are you going to be sure? Bad idea.
benzado
You could certainly be sure if you wrote the class, or you can use runtime introspection methods or similar approaches. I generally only use +initialize in my own source, I was just putting it out there as an option. Frequently, it *is* the best option, just not with categories.
Quinn Taylor
If you wrote the class, you don't need to put +initialize in a category. If you didn't write it, you can't trust that you won't be stomping on it in a future version of the library. Putting +initialize in a category is more risk than it's worth.
benzado
It probably is riskier and more trouble than it's worth in most cases, but I disagree that you'd *never* need/want to put `+initialize` in a category. For example, I may have written library/framework code, but only want the static variable and initialization in one client project. I was just trying to provide an alternative that is appropriate in some cases — let's not get militant about it... :-)
Quinn Taylor
+3  A: 
__attribute__((constructor))
static void initialize_navigationBarImages() {
  navigationBarImages = [[NSMutableDictionary alloc] init];
}

__attribute__((destructor))
static void destroy_navigationBarImages() {
  [navigationBarImages release];
}

These function will be called automatically when the program starts and ends.

KennyTM