The Stunning Utility of Global Classes
Set aside any ideas you may have that global data is evil. If you spend any time on the StackOverflow website, you’ve likely seen questions like “How do I share data from one view controller with another view controller?” The answer is: use a global class. It turns out that every class is a global class, if you just include the right header file. But for the cases I am thinking about, a global class for accessing common data or common methods can work out really well.
The kind of global data, or application data, I am referring to are things like
- app state
- data or image caches
- utility methods used in multiple controllers
- and there are many more uses!
I find that I usually end up with an AppData class, at least. Sometimes I also end up with an AppData class singleton, but you can avoid this by using file-static variables along with AppData class methods. We’ll see some examples below.
Convenience
One common need is to get a handle to your application delegate. Sure, you could do this every time you need it:
[code lang=”objc”]
#import "MyAppDelegate.h"
…
-(void)someMethod
{
MyAppDelegate *delegate = (MyAppDelegate *)
[[UIApplication sharedApplication] delegate];
…
}
[/code]
Or, you could do this instead:
[code lang=”objc”]
#import "AppData.h" // imports MyAppDelegate.h too
…
-(void)someMethod
{
MyAppDelegate *delegate = [AppData appDelegate];
…
}
[/code]
Where you also have:
[code lang=”objc”]
// AppData.h
#import "MyAppDelegate.h"
@interface AppData
+(MyAppDelegate *)appDelegate;
@end
[/code]
[code lang=”objc”]
// AppData.m
#import "AppData.h"
@implementation AppData
+(MyAppDelegate *)appDelegate
{
return (MyAppDelegate *)
[[UIApplication sharedApplication] delegate];
}
@end
[/code]
You might ask: Why wouldn’t I just put these kinds of class methods in my app delegate? The answer is: You certainly can. But, as you saw, just getting the handle to your delegate is a non-trivial line of code.
The real advantage is in how your code-writing time can be shortened simply by being able to type something shorter to accomplish your task. Typing AppData is shorter than typing MyVeryLongAppNameAppDelegate. 🙂
There are a myriad of class methods you could define in an AppData class both to simplify your code and make it easier to maintain. Consider custom colors that you need to use often. AppData class methods to the rescue. For example:
[code lang=”objc”]
+(UIColor *)myAppColor
{
return [UIColor colorWithRed: …];
}
[/code]
Not only have you made it easier to get this color where you need it, you have also made it very easy to tweak it and have your change take effect everywhere it’s used (since the color definition is in just one place.)
Singletons
The utility of a global class also lends itself nicely to singleton objects.
Consider some kind of data cache you need to maintain that you also need to access from multiple view controllers in your app. Easy! You might define your cache singleton class like this:
[code lang=”objc”]
// Cache.h
@interface Cache : NSObject
{
NSMutableArray *data;
}
+(NSMutableArray *)data;
@end
[/code]
[code lang=”objc”]
// Cache.m
@implementation Cache
static Cache *_cache = nil;
+(void)initialize
{
_cache = [[Cache alloc] init];
_cache.data = [[NSMutableArray alloc] init];
}
+(NSMutableArray *)data
{
return _cache.data;
}
@end
[/code]
Then to use this, you might do the following in your other code:
[code lang=”objc”]
#import "Cache.h"
…
-(void)someMethod
{
…
[[Cache data] addObject:…];
…
}
[/code]
To be sure, this example is a little contrived and the code as presented is certainly not complete nor devoid of some issues. But I think the idea is clear that with a global class you can achieve a number of benefits when it comes to code clarity, data sharing, and ease of maintenance.
Do you use global classes? What kinds of things do you use them for? If you don’t use them, why? Leave a comment!
In my experience, a common entry point for all application data tends to get unwieldy after awhile.
For things like application colors, I usually use a UIColor category (imported in prefix.pch), so I can do things like [UIColor myAppColor] wherever. It’s slightly more elegant than [Data myAppColor], in my opinion. Same things goes for fonts.
For application settings I either use NSUserDefaults or Core Data. The former only if the settings are minimal (which they almost never are).
If need access to commonly used methods, my answer is almost never “put them on a globally accessible class”. Rather, I like to put them close to objects they belong to, usually on models (fat models, slim controllers!) or categories.
For instance, let’s say I have a Core Data entity called Preferences (for user preferences). And assume the user can set the background color for the app. I store the color in the Core Data entity and add an instance method to the Preferences class that fetches the color.
I tend to use singletons for a few things though. Mainly to slim down my application delegate – and only if it really is something that should only be instantiated once. Things like persistent core data variables and a decorator for CLLocationManager, if I need one.
@Jesper – Thanks for your comment. I like the category idea; an elegant alternative! To be clear, regarding global classes, I certainly was not advocating that one should put _everything_ there. If there is data that belongs with a specific class, then by all means, it should go there! But if there is data, or a class of data like settings for example, that one needs to access from different, perhaps unrelated places, then I think a global class (or perhaps even a category) can work well. I do agree that a global class could become bloated over time. So like anything, these are techniques to use carefully.
In the end, and as usual, there are always many ways to solve these kinds of problems, and personal preference plus experience plays a major part in choosing one.
Thanks for posting your best practices here. I do appreciate that!
You’re right, a lot of it is a matter of preference. It’s certainly interesting to discuss. And best practices can always be further refined!
The reason I tend to shy away from globally accessible classes is that they tend to become everything-buckets. (“Where should I put it? It doesn’t fit anywhere. I’ll just stick it in the GlobalData, for now.”) I’d rather rethink the design if it comes to that – if it isn’t a model, maybe it should be.
I think categories are underestimated, in general. It’s a good way to group related methods, and you can even use instance variables (with the Objective-C runtime).
And singletons is a whole other can of worms! Though they are unavoidable, at times. 🙂
Oh yes, I do love categories. I did an app that had a substantial data set, derived from JSON via a RESTful API, that was handled simply with NSMutableDictionary. I wrote a category on NSMutableDictionary to provide accessors and such for the data that made writing the rest of the app a breeze and very easy to read and understand. Sounds like another blog post… 🙂
Caching UIColor like that doesn’t work if you use it on a background thread.