能否将单例设置回nil?

16

我使用了常规模式实现了一个单例对象。我的问题是:是否可以将此对象设置回nil,以便在稍后调用[MySingleton sharedInstance]时重新初始化对象?

我已经使用了正常的模式来实现单例对象。我的问题是,能否将此对象设置为nil,这样在稍后调用[MySingleton sharedInstance]时,对象会被重新初始化?
// Get the shared instance and create it if necessary.
+ (MySingleton *)sharedInstance {

    static dispatch_once_t pred;
    static MySingleton *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[MySingleton alloc] init];
    });
    return shared;
}

// We can still have a regular init method, that will get called the first time the     Singleton is used.
- (id)init 
{
    self = [super init];

    if (self) {
    // Work your initialising magic here as you normally would

    }
    return self;
}

我猜

MySingleton *shared = [MySingleton sharedInstance];
shared = nil;

仅将本地指针shared设置为nil。毕竟,shared被声明为static

3个回答

44

你对本地引用的假设是正确的,它不会影响你的单例。

为了能够重新初始化单例,你需要将静态变量移到方法外面,这样它就可以被整个类访问。

static MySingleton *sharedInstance = nil;
// Get the shared instance and create it if necessary.
+ (MySingleton *)sharedInstance {
    if (sharedInstance == nil) {
        sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}

+ (void)resetSharedInstance {
    sharedInstance = nil;
}

请注意,您不能再使用dispatch_once了,因为显然需要多次创建您的单例。如果您仅从UI(因此只从主线程)调用此单例,则上面的示例就可以了。

如果您需要从多个线程访问,请在+sharedInstance+resetSharedInstance方法周围放置锁,例如:

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

+ (void)resetSharedInstance {
    @synchronized(self) {
        sharedInstance = nil;
    }
}

这种方法比dispatch_once慢得多,但实际应用中通常并不会有太大影响。


5
是的,但是你的单例的sharedInstance方法将其定义为该方法内部的static,而你最终的代码示例仅将一个本地变量(巧合地也称为shared)设置为nil,未更改sharedInstance内部的static。因此,你只是将本地指针设为nil,而没有更改sharedInstance内部的static
如果你想做你所要求的事情,你需要将static变量sharedsharedInstance方法中提取出来(并且可能编写一些reset方法来将其设为nil)。你的sharedInstance方法也不能再依赖于dispatch_once,而是必须检查该static是否为nil

3

我这样做了。虽然我不确定这是最好的方法,但它似乎工作得很好。

static dispatch_once_t pred;
static MySingleton *shared = nil;

+(MySingleton *)sharedInstance {
        dispatch_once(&pred, ^{
            shared = [[MySingleton alloc] init];
        });
        return shared;
    }

+(void)clearSharedInstance {

       shared = nil;
       pred = nil;
}

1
是的和不是。你是正确的,dispatch_once_t 是一个 long,因此你应该使用 0 而不是 nil。但我想要强调的更大的问题是,“重置”调度一次谓词的概念是危险的。 - Alfonso
1
例如,在重置中存在竞争条件,可能会在您重置谓词的同时,有人已经再次调用了您的“+sharedInstance”方法。 - Alfonso
好吧,我必须以某种方式重置它。你有更好的方法吗? - Hackmodford
我建议您使用简单的锁定,而不是使用@synchronize ,并提供一个重置方法。 dispatch_once 不打算被多次执行(因此名称就是这样;-))。 - Alfonso
像这里的第二个答案怎么样?http://stackoverflow.com/questions/16491326/force-deletion-of-singleton-in-test-code-that-was-created-with-dispatch-once - Hackmodford
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接