views:

87

answers:

2

Generally, I use static arrays and dictionaries for containing lookup tables in my classes. However, with the number of classes creeping quickly into the hundreds, I'm hesitant to continue using this pattern. Even if these static collections are initialized lazily, I've essentially got a bounded memory leak going on as someone uses my app.

Most of these are arrays of strings so I can convert strings into NSInteger constants that can be used with switch statements, etc.

I could just recreate the array/dictionary on every call, but many of these functions are used heavily and/or in tight loops.

So I'm trying to come up with a pattern that is both performant and not persistent.

If I store the information in a plist, does the iphoneOS do anything intelligent about caching those when loaded?

Do you have another method that might be related?

EDIT - ANSWER EXAMPLE

Based on a solution proposed below, here's what I'm going to work with...

First, add a method to NSObject via category.

- (void)autoreleaseOnLowMemory;

Now, whenever I want to create lazy-loading static array or dictionary in a helper function, I can just use the following pattern...

- (id)someHelperFunction:(id)lookupKey {
    static NSDictionary *someLookupDictionary = nil;
    if (!someLookupDictionary) {
        someLookupDictionary = [[NSDictionary dictionaryWithObjects:X, Y, Z, nil] autoreleaseOnLowMemory];
    }
    return [someLookupDictionary objectForKey:lookupKey];
}

Now, instead of that static dictionary living until the end of the program, if we're running out of memory it will be released, and only re-instantiated when needed again. And yes, in a large project running on an iphone, this can be important!

PS - The implementation of autoreleaseOnLowMemory is trivial. Just create a singleton class with a method that takes an object and retains it in a set. Have that singleton listen for low memory warnings, and if it gets one, release all the objects in that set. May want to add a manual release function as well.

A: 

If you are just doing strings, you could use C arrays.

id keys[] = { @"a" , @"b" , @"c" };
id values[] = { @"1" , @"2" , @"3" };

And if you occasionally need a true NSArray or NSDictionary from that:

[NSArray arrayWithObjects:values count:3];
[NSDictionary dictionaryWithObjects:values forKeys:keys count:3];

A plist will involve a disk hit and xml parsing for each collection. As far as I know only NSUserDefaults are cached.

drawnonward
Nothing wrong with this answer. It would decrease the overhead a bit to not have the arrays wrapped in objects, but not significantly. Again, not a bad idea, I just think the other solution proposed better suits my needs.
DougW
+2  A: 

I generally prefer plists for this just because they're easy to maintain and reuse in different sections of code. If the speed of loading them into an NSDictionary from file is a concern (and check the profiler to be sure) you can always put them into an instance variable which you can release when you get a memory warning.

Marc Charbonneau
Ah, I like this. I think I've got an interesting solution... I believe I'll make my own long-term autorelease pool via a category on NSObject that only releases when a memory warning is received. Since I use it on lazily loaded objects, it shouldn't create any kind of thrashing unless the device doesn't have enough memory to even show the current scenario. I'll add a code example to my original question. Thanks!
DougW