开发者

Singleton or Class methods [duplicate]

This question already has answers here: What does @synchronized() do as a singleton method in objective C? (6 answers) Closed 3 years ago.

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开发者_Go百科 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.


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.


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];
}


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. =)


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
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜