17

It seems a lot of Objective-C code is using Singleton nowadays.

While a lot of people complaining about Singleton, e.g. Google (Where Have All the Singletons Gone?), their fellow engineers also use it anyway: http://code.google.com/mobile/analytics/docs/iphone/

I know we had some answers in Stack Overflow already but they are not totally specific to Objective-C as a dynamic language: Objective C has categories, while many other languages do not.

So what is your opinion? Do you still use Singleton? If so, how do you make your app more testable?

Updated: I think we need to use codes as example for more concrete discussion, so much discussions on SO are theory based without a single line of code

Let's use the Google Analytics iOS SDK as an example:

// Initialization
[[GANTracker sharedTracker] startTrackerWithAccountID:@"UA-0000000-1"
                                        dispatchPeriod:kGANDispatchPeriodSec
                                              delegate:nil];
// Track page view
[[GANTracker sharedTracker] trackPageview:@"/app_entry_point"
                                   withError:&error];

The beauty of the above code is once you have initialized using the method "startTrackerWithAccountID", you can run method "trackPageview" throughout out your apps without passing through configurations.

If you think Singleton is bad, can you improve the above code?

Much thanked for your input, have a happy Friday.

Howard
  • 19,215
  • 35
  • 112
  • 184
  • This should be moved to programmers.stackexchange.com – coneybeare May 06 '11 at 14:31
  • you can improve on your example in several ways, depending on your goals. You could make it a plain global instead of a singleton, and you'd still be able to do the same things, but you would *also* get more flexible and more testable code. Or you could, at the cost of requiring more typing, pass the otherwise global data as parameters, which leads to conceptually cleaner and better structured code, but more typing and more overhead in writing the code. – jalf May 06 '11 at 15:41
  • I don't see anything wrong with that Singleton code. You're not pulling data from it, so it's not getting used as global storage. I'd be worried if you were sticking view controllers or domain objects in there. – Terry Wilcox May 06 '11 at 15:58

4 Answers4

34

This post is likely to be downvote-bait, but I don't really understand why singletons get no love. They're perfectly valid, you just have to understand what they're useful for.

In iOS development, you have one and only one instance of the application you currently are. You're only one application, right? You're not two or zero applications, are you? So the framework provides you with a UIApplication singleton through which to get at application-level os and framework features. It models something appropriately to have that be a singleton.

If you've got data fields of which there can and should be only one, and you need to get to them from all over the place in your app, there's totally nothing wrong with modeling that as a singleton too. Creating a singleton as a globals bucket is probably a misuse of the pattern, and I think that's probably what most people object to about them. But if you're modeling something that has "singleness" to it, a singleton might well be the way to go.

Some developers seem to have a fundamental disgust for singletons, but when actually asked why, they mumble something about globals and namespaces and aesthetics. Which I guess I can understand, if you've really resolved once and for all that Singletons are an anti-pattern and to be abhorred in all cases. But you're not thinking anymore, at that point. And the framework design disagrees with you.

Dan Ray
  • 21,623
  • 6
  • 63
  • 87
  • 1
    +1 and I'm a guy who recommends against Singletons 90% of the time. – Terry Wilcox May 06 '11 at 15:10
  • +1 - Your answer is very balanced and also quite detailed. However I think it is a good approach to tell new programmers not to rely on singletons. If they think they need one, it has to be justified. So your answer is perfectly valid, for me the short advice is still "try to avoid singletons, unless..." – GorillaPatch May 06 '11 at 15:18
  • 1
    @Dan yes, you have only one application, but why do you need to model your application in the application? The process is also running on one, and only one CPU, in one, and only one country, on one, and only one planet. So f'ing what? Why does your application need an "application instance"? And what benefit do you gain by making that instance a singleton? The best you can say is that "there's no harm in it"... other than the additional complexity a singleton brings. But it doesn't give you anything positive to make up for it – jalf May 06 '11 at 15:37
  • If you have a field where there can and should be only one, then you create one and only one. End of story. That's all you need to do, and going further only 1) makes your code more fragile, and 2) makes it harder to maintain if and when it turns out that you *did* after all need two instances – jalf May 06 '11 at 15:38
  • Here's the most concrete, and "non.mumbly" answer I can give you. Let me know if it satisfies you, and if not, what flaws you find in it: http://jalf.dk/blog/2010/03/singletons-solving-problems-you-didnt-know-you-never-had-since-1995/ – jalf May 06 '11 at 15:38
  • @jalf - I find a couple flaws. First, you've confused global ACCESS with global SCOPE. Properties of a global object have their own scope, and can be gotten to globally. All the railing against "globals", while accurate, is irrelevant. Second, you successfully describe some common places where Singletons shouldn't be used, and therefore conclude that they should NEVER be used, which just isn't a logical conclusion. – Dan Ray May 06 '11 at 18:31
  • @jalf - Re modeling the application instance, that's how you get to lots of framework services in iOS. I didn't design it, I'm just saying, how you get to lots of bits of data and functions having to do with the process you are is via a UIApplication singleton. To me, that seems entirely appropriate. – Dan Ray May 06 '11 at 18:34
  • @Dan: hmm? If global *SCOPE* allows an object to be "gotten to globally", then how is that not "global *ACCESS*"? Why is the railing against globals irrelevant? And what is a valid use case for singletons? You just have to come up with a single counterexample, after all, so you have it easy. ;) You haven't mentioned a single concretwe flaw yet, because you haven't said anything specific. Just "this is irrelevant", and "this isn't the same as that", with zero explanation. :) – jalf May 06 '11 at 18:35
  • @Dan I accept that some APIs require you to create instances of this or that, whether it makes sense. But it doesn't logically follow that this should be a singleton. Does *every* part of your applicatino need to communicate with the Application instance? That, to me, sounds downright scary. (And, again, even if that is necessary, why not make it a plain old global, instead of a singleton?) – jalf May 06 '11 at 18:36
  • @jalf - Our comments crossed in the mail. I say the UIApplication singleton is a great example of a valid Singleton use case. Cocoa Touch (and for that matter Objective-C) is fairly idiomatic, and Singletons are a relatively common design pattern they use--or at least, I get the impression other languages use them way less. Accessing the UIApplication instance is not *super* common, but it's used for, eg, configuring background execution behavior, registering the app for remote notifications, controlling some app-wide appearance settings, etc. – Dan Ray May 06 '11 at 18:36
  • 1
    @Dan: and I say why? As I said in my blog post, a singleton ensures that 1) exactly one instance exists at any time, throughout the lifetime of the application. And yes, that makes logical sense when you run the app, but it's not necessary to *enforce*. And it makes no sense when testing, where you'd want to mock it out, replacing it with several short-lived mock objects. That's virtually impossible with a singleton. And 2) it means that *every* part of your application can access the application instance. Usually, you want to minimize the contact surface agaionst external APIs – jalf May 06 '11 at 18:40
  • 2
    re. #1, people often get it wrong by thinking that "because I only need one instance, it would be beneficial to make it *impossible* to create two instances". That just doesn't logically follow. If you only need one instance, you *create* only one instance. That way, once you need two instances (for testing, for example), you haven't shot yourself in the foot – jalf May 06 '11 at 18:41
  • Here's the other thing people miss about singletons--when you grab your one instance of it, the state you set in it before is carried along. Yes there are other ways to do that, but for my money that's one of the most convenient ways to carry a bundle of unique data around an app. – Dan Ray May 06 '11 at 18:42
  • (This is an interesting conversation but I actually have to get some work done now! I'll check back in later.) – Dan Ray May 06 '11 at 18:44
  • A good use for a singleton: A logging system where information flows one way and all reachable objects are immutable. It's used from everywhere so it's convenient and causes no harm. Want to test it? create an instance using a normal constructor. EDIT: I just read your blog post, but I would still use a logging singleton. I could discriminate between classes turning my log call into a singleton that passes self. Currently NSLog is serial. Going further would require a log system like those on Java with external config. Still not bad to use it as singleton for convenience. – Jano Jun 30 '13 at 04:20
33

I think most developers go through the Singleton phase, where you have everything you need at your fingertips, in a bunch of wonderful Singletons.

Then you discover that unit testing with Singletons can be difficult. You don't actually want to connect to the database, but your Singleton does. Add a layer of redirection and mock it.

Then you discover that unit testing isn't the only time you need different behaviour. You make your Singleton configurable to have different behaviour based on a parameter. You start to wonder if you need to split it into two Singletons. Then your code needs to know which Singleton to use, so you need a Singleton that knows which Singleton to use.

Then some other code starts messing with the values in your Singleton, while you're using it. How dare they! If you wanted just anybody to get at those values from anywhere, you'd make them global...

Once you get to this point, you start wondering if Singletons were the right solution. You start to see the dangers of global data, particularly within an OO design, where you just assume your data won't get poked at by other people.

So you go back and start passing the data along, rather than looking it up (this used to be called good OO design, but now it has a fancy name like "Dependency Injection").

Eventually you learn that Singletons are fine in moderation. You learn to recognize when your Singleton needs to stop being single.

So you get shared objects like UIApplication and NSUserDefaults. Those are good uses of Singletons.

I got burned enough in the Java Singleton craze a decade ago. I don't even consider writing my own Singletons. The only time I've needed anything similar in recent memory is wanting to cache the result of [NSCalendar currentCalendar] (which takes a long time). I created a category on NSCalendar and cached it as a static variable. I felt a bit dirty, but the alternative was painfully slow code.

To summarize and for those who tl;dr:

Singletons are a tool. They're not likely to be the right tool, but you have to discover that for yourself.

Terry Wilcox
  • 9,010
  • 1
  • 34
  • 36
  • 1
    Good answer. The downfall of singletons in Objective-C is definitely unit testing. Each class needs to know how to instantiate another -- for example, calling `[[Singleton sharedSingleton] doSomething]`. That means that you can't readily replace your singleton with a mock object for unit tests. – asinesio May 06 '11 at 15:50
  • Great answer. An off-topic note on the NSCalendar trick, make sure to spawn new calendars on background threads, as NSCalendar is not thread safe. – epologee Jun 08 '12 at 07:23
  • It's so true. I remember when I was young and bulletproof with my several singletons per project. I can't exactly say what happened, but I can say I haven't used one in years... – Paul de Lange Jan 15 '13 at 09:44
  • Very good answer, would like to add that the problem of singletons is not only unit testing(in ObjC we could use method swizzling replacing the sharedSingleton method for one returning a mock), but they make dependencies hidden and this is a big drawback comparing to use DI where the dependencies are visible and you can notice with ease when a class has too many. – e1985 Mar 12 '14 at 12:28
3

Why do you need an answer that is "total Objective C specific"? Singletons aren't totally Obj-C specific either, and you're able to use those. Functions aren't Obj-C-specific, integers aren't Obj-C specific, and yet you're able to use all of those in your Obj-C code.

The obvious replacements for a singleton work in any language.

A singleton is a badly-designed global.

So the simplest replacement is to just make it a regular global, without the silly "one instance only" restriction.

A more thorough solution is, instead of having a globally accessible object at all, pass it as a parameter to the functions that need it.

And finally, you can go for a hybrid solution using a Dependency Injection framework.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • What are some dependency injection frameworks for Objective-C besides the Apple-provided ones? I've yet to see anything that compares to Spring (from Java) in the Objective-C world. – asinesio May 06 '11 at 15:46
  • no clue. I'm not a huge fan of DI frameworks. But they're a popular option, so I thought I'd mention them. :) – jalf May 06 '11 at 17:06
  • @asinesio Take a look at http://typhoonframework.org, a DI framework for Objc. (we also start by explaining how to do DI without a library). – Jasper Blues May 26 '14 at 00:35
1

The problem with singletons is that they can lead to tight coupling. Let's say you're building an airline booking system: your booking controller might use an

id<FlightsClient>

A common way to obtain it within the controller would be as follows:

_flightsClient = [FlightsClient sharedInstance];

Drawbacks:

  • It becomes difficult to test a class in isolation.
  • If you want to change the flight client for another implementation, its necessary to search through the application and swap it out one by one.
  • If there's a case where the application should use a different implementation (eg OnlineFlightClient, OfflineFlightClient), things get tricky.

A good workaround is to apply the dependency injection design pattern.

Think of dependency injectionas telling an architectural story. When the key actors in your application are pulled up into an assembly, then the application’s configuration is correctly modularized (removing duplication). Having created this script, its now easy to reconfigure or swap one actor for another.”. In this way we need not understand all of a problem at once, its easy to evolve our app’s design as the requirements evolve.

Here's a dependency injection library: https://github.com/typhoon-framework/Typhoon

Jasper Blues
  • 28,258
  • 22
  • 102
  • 185