Learn the memory management rules. They are simple and in a week you’ll have the time investment back.
Forget about singletons. More precisely, forget about singletons enforced by code. They are unnecessarily complicated and 99% of the time are simply bad design.
Read Singletons are Pathological Liars by Miško Hevery, read it carefully, read all of the related blog posts and think about it. Most of the time there is no real need for a class to enforce having a single instance. Usually you can get around the problem by creating some kind of Factory class that will create most instances for you and wire them together:
@interface Factory {
id classYouWantJustOneInstanceOf;
}
- (id) wireMainController;
@implementation Factory
- (id) init {
[super init];
// No “sharedFoo” stuff necessary. Foo is a plain
// class without singleton boilerplate, easily testable.
classYouWantJustOneInstanceOf = [[Foo alloc] init];
return self;
}
- (id) wireMainController {
id controller = [[SomeClass alloc] init];
// Dependencies set explicitly, good.
[controller setFoo:classYouWantJustOneInstanceOf];
return [controller autorelease];
}
@implementation UIApplicationDelegate
- (void) applicationDidFinishLauching: (UIApplication) app {
// Now all your “singletons” will get created,
// no funny static stuff.
factory = [[Factory alloc] init];
controller = [[factory wireMainController] retain];
[window addSubview:controller.view];
// up and running
}
- (void) dealloc {
[controller release];
// Now all your “singletons” will get released.
[factory release];
[super dealloc];
}
I know this feels a bit uneasy compared to “simple” [Foo sharedFoo]
, but it’s worth it, trust me.
The advantages of this solution, in case they are not apparent:
No singleton boilerplate in your classes. No static shared instances, no thread synchronization, nothing.
Explicit dependency management. If class A needs instance of B to do its work, you can see that from a public setter or a constructor parameter of A. No suprise dependencies inside the implementation file. And if you need to wire A differently, say for testing purposes, it’s easier when the dependencies are explicit (as opposed to calling [B sharedB]
).
Clear object lifecycle. No static variables, no static initialization and no lazy initialization unless you really want it. You know when objects get created and you can deallocate everything you create.
Note that this is a simplified case, so that the division between application delegate and Factory seems a bit pedantic, but in a real case it makes perfect sense (see the Principle of single responsibility).