6

After reading the responses to a question about singletons in Objective C it appears that each solution makes some tradeoff in regards to threading in the instance accessor. i.e.

@synchronized(self)
{
    if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;

This essentially single-threads access to the singleton, and if it's something that's used frequently in an operation, seems like something that could cause threads to unnecessarily contend.

What's the downside to simply using the class object as the singleton instance, and exposing functionality via class methods, i.e.

@interface MySingleton : NSObject {
}

+ (void)doSomething;
@end

@implementation MySingleton    
+ (void)initialize {
   //do some setup if necessary
}

+ (void)doSomething {
    //do something
}
@end

In this way we avoid doing the lock + check every time we want to reference the singleton object and we can also eliminate having to store it in a local or method ivar.

This approach also lets the runtime guarantee that only one instance (the Class object) exists in the system at any given time.

EDIT

There's more here than just threading, with a traditional singleton you usually write code like this:

MySingleton *instance = [MySingleton getSharedInstance];
NSObject *someResult = [instance getResult];
//or
if (instance.someProperty) {
  //do something
}

However if your singleton is a class instance, you essentially eliminate the need call getSharedInstance all the time. Consider this code:

NSObject *someResult = [MySingleton getResult];
//or
if ([MySingleton someProperty]) {
  //do something
}

I hear the point that you have to store your data in file local static variables, or in global variables (yuck). But it's really not all that different from a traditional singleton with the exception that you lose Objective-C 2.0 properties (instead you have to use traditional accessor methods).

Here's one key tradeoff for me that seems like a win. In a traditional singleton you end up overriding -copyWithZone, +allocWithZone, -retain, -retainCount, -release and -autorelease if you really want to get things right.

This seems like an awful lot of work to do every time you want to write a simple Singleton object (they happen to be pretty useful). So why not simply just replace it with this:

@implementation MySingleton
+ (void)initialize {
    //do your setup
}

- (id)init {
    NSAssert(NO, @"You should read the documentation on singletons.");
}
@end

It's a lot lighter in terms of code, and unless your consumers are really sneaky they won't ever create two instances.

Get to the point already My question is really this:

Is there any drawback to using the Class object as the instance of your singleton?

It seems like you can take all the same steps in terms of threadsafety, memory efficiency etc. without having to remember to override so many methods and accessors or litter your code with instance checks.

Community
  • 1
  • 1
ImHuntingWabbits
  • 3,827
  • 20
  • 27
  • I use the top method. If I know I am going to make heavy use of the singleton in a method, I just get it and assign it to a local variable. – JeremyP Nov 23 '10 at 09:29
  • I don't think there's anything wrong with your theory here, but you just need to realize, someone coming into your code will be somewhat confused as to what is going on -- you're calling class methods but acting as if you were an object. – slycrel Nov 24 '10 at 03:46
  • @slycrel Agreed. I generally try to write a lot of comments, so I think that would help. Of course I'm assuming someone will read those comments which may turn out in practice to be false. – ImHuntingWabbits Nov 24 '10 at 06:57

4 Answers4

5

With iOS 4.0 or later, by far the best solution is to just use dispatch_once, as in

+ (id)sharedInstance {
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedInstance = [[MyClass alloc] init];
    });
    return sharedInstance;
}

You may also want to consider using a single dispatch_queue to serialize access to the internals of a class. If all your public methods just run a block on the same dispatch_queue then you won't have to worry about concurrency issues.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 1
    I think that definitely solves the issue of threadsafety, however I don't think I mean for that to be the meat of my question, I've amended it accordingly. Thanks! – ImHuntingWabbits Nov 23 '10 at 07:48
  • I don't think I'd want to have to run a block for *every* public method. I think `@synchronized` is clearer. – JeremyP Nov 23 '10 at 09:27
  • 2
    @JeremyP: the block only gets run once. (Hence the "once" in `dispatch_once`. This proposal is far more efficient than `@synchronized` for something that only needs to happen once. – BJ Homer Sep 28 '11 at 17:11
  • @BJ Homer: Can you quantify the efficiency gains? Unless it really is a performance bottlneck, I think `@synchronized` is clearer. – JeremyP Sep 29 '11 at 08:48
  • 3
    @JeremyP `dispatch_once` is 3x faster in single-threaded tests, and up to 20x faster in multi-threaded tests. See http://bjhomer.blogspot.com/2011/09/synchronized-vs-dispatchonce.html. – BJ Homer Sep 29 '11 at 14:44
  • @JeremyP: Not to mention that dispatch_once is actually clearer semantically. It's saying "Run this once, ever", whereas a `@synchronized` block requires reading the logic inside of it to see what's going on. Note that a `@synchronized` block also requires taking a lock whereas dispatch_once() only has to use a single atomic-compare-and-swap to figure out that the block has already been run (i.e. dispatch_once doesn't call into the kernel ever). – Lily Ballard Sep 29 '11 at 18:05
  • @BJ Homer: Quantify it in a real world situation. What is the gain of (say) three milliseconds to one millisecond if the method is only invoked a handful of times? If it's called 100 times a second, fair enough. – JeremyP Sep 30 '11 at 10:58
  • 1
    @JeremyP In practice, you're right that it's probably not going to be a bottleneck. I still think that `dispatch_once` communicates "run this code once" better than `@synchronized(self) { if (!sharedInstance) { ... } }` does. – BJ Homer Sep 30 '11 at 16:38
3

This is my first post on Stack Overflow... (so prepare for stupidity)

I think there is a hybrid solution that might be useful.

I want to set and get (global) values out of a singleton class without having calling "getSharedInstance". I'd want the code to look like this...

frameRate = Singleton.frameRate;
Singleton.frameRate = 42;

To achieve this, each variable we need to store in the singleton has a getter and setter class method. The class method then goes to an instance to store the data in an ivar. The instance isn't directly accessed by the main program.

The getter looks like this:

+ (int) frameRate
{
    return [[Singleton instance] ivarFrameRate];
}

The (ugly) instance call is hidden inside the class code.

By calling the instance method here, the class method will automatically instantiate an object when first used. Once the singleton is instantiated, the instance stores ivars conventionally. Here, I am prefixing with "ivar" make the ivar explicit.

@property  int ivarFrameRate;

and

@synthesize ivarFrameRate;

This automatically creates conventional getter (and setter) methods to access the ivar.

(edit - here is a complete example)

//  Singleton.h
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
{
float ivarFrameRate
}

@property  float ivarFrameRate;

- (id) init;
+ (Singleton *) instance;
+ (float) frameRate;
+ (void) setFrameRate:(float)fr;
@end

and

//  Singleton.m
#import "Singleton.h"
@implementation Singleton
@synthesize ivarFrameRate;

static Singleton* gInstance = NULL;

+ (Singleton*)instance
{
    @synchronized(self)
    {
        if (gInstance == NULL)
        gInstance = [[self alloc] init];
    }
    return(gInstance);
}


- (id)init
{
    self = [super init];
    return self;
}

+ (float) frameRate
{
    return [[Singleton instance] ivarFrameRate];
}

+ (void) setFrameRate:(float)fr;
{
    [[Singleton instance] setIvarFrameRate:fr];
}
Glyn Williams
  • 126
  • 1
  • 7
  • That does not compile. Classes can't have properties in objective C, and the compiler does not know to change Singleton.frameRate into [Singleton frameRate] – ImHuntingWabbits Oct 28 '11 at 22:09
  • I can assure you it does compile and I am using a singleton for making a handful of variables global. It's a little long winded, because each getter and setter is effectively written twice. The single object instance stores the variable as an ivar. The properties relate to the instance. The (+) class methods then set and get the variable from the single instance. – Glyn Williams Oct 29 '11 at 12:50
  • Please post the code in entirety, I'm still not able to get Singleton.frameRate to compile. – ImHuntingWabbits Nov 02 '11 at 22:48
  • I have added the full code to my post.(above) – Glyn Williams Nov 10 '11 at 12:58
  • 1
    After much procrastination I have copied your code into a test project and stand correct, it works like a charm. I think this solution then would provide a more elegant solution to the problem I was struggling with. Ten points for Gryffindor! – ImHuntingWabbits Jan 27 '12 at 08:06
0

This is fine, but still just changes your circumstances rather than fixes your problems. Unless you don't have any actual data tied to your singleton, in which case this will work just fine. Anytime you access central data you will need to properly make it thread-safe.

Additionally, without some kind of iVar I don't know of a way to store data (that is intended) directly in a class.

In the example above I would code it in this way, getting the same result as you are proposing and only taking the performance hit if we are creating/re-creating the singleton:

if (sharedInstance)
     return sharedInstance;

@synchronized(self)
{
    if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;

Keep in mind that either way, if you are accessing data that is potentially changing on different threads then you'll have to make that code thread-safe anyway, either with very careful planning or using code to ensure there are no problems. I'd recommend a mix, but when in doubt the latter when at all possible. =)

slycrel
  • 4,275
  • 2
  • 30
  • 30
  • Central data can be stored as a file local static variable and if appropriate wrapped with a locking scheme. In my experience it seems that different pieces of data may require different locking schemes to make access efficient. – ImHuntingWabbits Nov 23 '10 at 01:05
  • 1
    This pattern (checking the flag before entering the `@synchronized` block) is known as double-checked locking, and is broken. Specifically, the `sharedInstance` pointer may be set before the object is fully initialized. See http://www.mikeash.com/pyblog/friday-qa-2009-10-02-care-and-feeding-of-singletons.html – BJ Homer Sep 29 '11 at 22:55
  • Thanks for pointing that out, I've never run across nor seen a problem doing it this way. It's good to know that in some specific circumstances it doesn't work and what the solution would be. A year later, I think I'd prefer to use the dispatch_once call to take care of this as mentioned above and by mike ash. – slycrel Sep 30 '11 at 02:39
  • If you google "double check locking" for various languages there are a few good white papers on the pitfalls of this scheme. Most lead to memory leaks while others can deadlock in certain situations. Failure is rare but costly. – ImHuntingWabbits Oct 28 '11 at 22:00
0

If you use a class as your singleton, the only way to store data would be to use static file variables and global variables. If you are going to go so far that you make a class you don't plan to instantiate, you might as well just use standard C functions:

void doSomething(void);

void doSomething() {
    //do something
}
ughoavgfhw
  • 39,734
  • 6
  • 101
  • 123