0

我不太确定这个实现哪里出了问题,以及我需要做哪些调整才能使加载的单例留在内存中。

@implementation ApplicationSettings

static ApplicationSettings *sharedSettings = nil;

+ (void)load
{
    @autoreleasepool {
        sharedSettings = [self sharedSettings];
        [self configureInitialSettings];
    }
}

+ (ApplicationSettings*) sharedSettings {
    if (sharedSettings)
        return sharedSettings;
    else {
        return [[[ApplicationSettings alloc] init] autorelease];
    }
}

- (void) dealloc {
    [super dealloc];
}

这里显示了主要的加载方法,以及 dealloc(我没有使用 ARC)。

我最近开始使用这种模式。我将不得不检查每个实现以确保它是正确的。我发现了一种情况,它被释放并导致崩溃。显然@autoreleasepool 拥有它。我应该删除 return 语句中的自动释放,使其不进入自动释放池吗?如果我这样做,那么它会一直保留到程序被杀死,这对我来说很好。这有什么问题吗?

4

1 回答 1

1

这不是声明单例类的正确方法。(因此,您的+load方法和对象保留不适合您在此处的使用以及崩溃的原因)

  • 在方法中加载单例实例+load是没有用的,因为最好只在需要时分配实例,即在第一次请求时(延迟加载)
  • 但更重要的是,在方法中返回一个自动释放的实例sharedSettings会使单例模式无效并无法达到其目的,因为您返回的实例将在自动释放池耗尽时立即释放(即在当前 RunLoop 迭代结束时) - 如果你有任何内部@autoreleasepool使用,如果不是更早)。这很可能是你崩溃的原因!
  • 的实现dealloc是无用的,首先因为它只调用它的super实现(这样你可以避免整个方法定义),并且因为你的类是一个单例,当你的应用程序还活着时,实例永远不会从内存中释放。
  • 最后但并非最不重要的一点是,您的实现根本不是线程安全的。

当然,通常你需要平衡allocinit调用releaseautorelease调用。这是内存管理的主要规则。但是对于单例模式,根据定义,只要应用程序存在(这是主要角色),单例的共享实例就会存在,这是规则的例外,您不应释放包含单例对象的 sharedInstance,以确保它继续存在并且不会从内存中释放。


没有 ARC 的单例的一种正确(传统)实现是重载alloc/ retain/ release/ autorelease/retainCount方法,如 Apple 关于单例的文档中所述。但事实上,这并不是我们现在真正这样做的方式,因为这个文档已经很老了(在 GCD 存在之前编写)并且已经过时,而且GCD 现在提供了一种更好的替代方案,即保证线程安全,并且也与非ARC和ARC兼容(而后者在ARC环境下无法实现)。所以这里是:

@implementation ApplicationSettings
+(ApplicationSettings*)sharedInstance
{
    static ApplicationSettings* sharedInstance = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{ sharedInstance = [[self alloc] init]; });
    return sharedInstance;
}
@end

就这样。不需要方法之外的全局变量或其他任何东西。只需声明此方法并在每次需要访问单例时使用它即可。如果您需要填充单例的一些实例变量,就像您可能在您的configureInitialSettings方法中所做的那样,只需在init您的单例的方法中完成,就像您在标准类中做的那样。


附加说明:如果您想非常严格,有些人可能会争辩说它不会禁止使用 alloc/init 分配/创建该类的其他实例(您自己的实现也不会在您的问题中)所以这不是真正的单例:如果她/他真的想要,仍然可以分配它的多个实例。

但在实践中,即使这是真的,我也看不出有什么理由要真正添加这些约束(如果有一天你切换到 ARC,你无论如何都不能添加这些约束)。当您寻求单例模式时,您真正想要的更多是“在整个应用程序中共享的公共实例”,而不是“一种禁止创建多个实例的方法”,这就是我们在这里所拥有的。实际上,对于大多数声称是单例的类(NSFileManager例如)来说,情况就是如此。

于 2012-09-16T00:14:49.330 回答