单例或类方法

时间:2010-11-22 23:02:11

标签: objective-c singleton

在阅读了对目标C中关于单身人士question的回复之后,似乎每个解决方案都在实例访问者中对线程进行了一些权衡。即。

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

这基本上是单线程访问单例,如果它是在操作中经常使用的东西,似乎可能会导致线程不必要地竞争。

简单地使用类对象作为单例实例并通过类方法公开功能有什么缺点,即

@interface MySingleton : NSObject {
}

+ (void)doSomething;
@end

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

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

这样我们每次想要引用单例对象时都避免进行锁定+检查,我们也可以省去将其存储在本地或方法ivar中。

此方法还允许运行时保证在任何给定时间系统中只存在一个实例(Class对象)。

修改

这里不仅仅是线程,使用传统的单例,您通常会编写如下代码:

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

但是,如果你的单例是一个类实例,你基本上不需要一直调用getSharedInstance。请考虑以下代码:

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

我听说你必须将数据存储在文件本地静态变量或全局变量(yuck)中。但它确实与传统的单例不同,除了你丢失了Objective-C 2.0属性(而是你必须使用传统的访问器方法)。

这对我来说是一个关键的权衡,似乎是一场胜利。在一个传统的单例中你最终会覆盖-copyWithZone,+ allocWithZone,-retain,-retainCount,-release和-autorelease,如果你真的想要把事情做好。

每次想要编写一个简单的Singleton对象时,这似乎都是很多工作(它们恰好非常有用)。那么为什么不简单地用它替换它:

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

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

代码方面要轻松得多,除非你的消费者真的偷偷摸摸,否则他们不会创建两个实例。

已经达到目的 我的问题是这样的:

使用Class对象作为单例的实例有什么缺点吗?

似乎你可以在线程安全性,内存效率等方面采取所有相同的步骤,而不必记住覆盖如此多的方法和访问器,或者使用实例检查来丢弃代码。

4 个答案:

答案 0 :(得分:5)

对于iOS 4.0或更高版本,到目前为止最好的解决方案是使用dispatch_once,如

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

您可能还想考虑使用单个dispatch_queue来序列化对类的内部的访问。如果所有公共方法只在同一个dispatch_queue上运行一个块,那么你就不必担心并发问题了。

答案 1 :(得分:3)

这是我关于Stack Overflow的第一篇文章......(所以准备愚蠢)

我认为有一种可能有用的混合解决方案。

我想在没有调用“getSharedInstance”的情况下从单例类中设置和获取(全局)值。我希望代码看起来像这样......

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

为了实现这一点,我们需要存储在单例中的每个变量都有一个getter和setter类方法。然后,类方法转到实例以将数据存储在ivar中。主程序不直接访问该实例。

getter看起来像这样:

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

(丑陋的)实例调用隐藏在类代码中。

通过在此处调用实例方法,类方法将在首次使用时自动实例化对象。一旦实例化单例,实例就会按常规存储ivars。在这里,我用“ivar”作为前缀,使ivar明确。

@property  int ivarFrameRate;

@synthesize ivarFrameRate;

这会自动创建传统的getter(和setter)方法来访问ivar。

(编辑 - 这是一个完整的例子)

//  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

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

答案 2 :(得分:0)

这很好,但仍然只是改变你的情况而不是修复你的问题。除非你没有任何与你的单身人士有关的实际数据,否则这将很好。无论何时访问中央数据,您都需要正确地使其成为线程安全的。

此外,如果没有某种iVar,我不知道如何直接在类中存储数据(这是预期的)。

在上面的示例中,我将以这种方式对其进行编码,获得与您提议的结果相同的结果,并且只有在我们创建/重新创建单例时才会获得性能:

if (sharedInstance)
     return sharedInstance;

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

请记住,无论哪种方式,如果您正在访问可能在不同线程上发生变化的数据,那么无论如何您都必须使该代码成为线程安全的,无论是仔细规划还是使用代码来确保没有问题。我建议混合使用,但是当有疑问时,后者尽可能。 =)

答案 3 :(得分:0)

如果使用类作为单例,则存储数据的唯一方法是使用静态文件变量和全局变量。如果你要去做一个你不打算实例化的课程,你也可以只使用标准的C函数:

void doSomething(void);

void doSomething() {
    //do something
}