【问题标题】:Apple Singleton example query?Apple Singleton 示例查询?
【发布时间】:2010-03-09 16:52:47
【问题描述】:

我对这段在创建单例实例时覆盖某些方法的 sn-p 代码(在 CocoaFundamentals 指南中介绍)感到有些困惑。

static id sharedReactor = nil;

+(id)sharedInstance {
    if(sharedReactor == nil) sharedReactor = [[super allocWithZone:NULL] init];
    return sharedReactor;
}

.

+(id)allocWithZone:(NSZone *)zone {
    return[[self sharedInstance] retain];
}

-(id)retain {
    return self;
}

在创建单例实例的代码中,+sharedInstance 方法从超类(在我的例子中是 NSObject)调用 [super allocWithZone:NILL]单身人士。

我感到困惑的是保留的使用,特别是看到保留也被覆盖以返回自我。谁能解释一下,能不能不写:

+(id)allocWithZone:(NSZone *)zone {
    return [self sharedInstance];
}

-(id)retain {
    return self;
}

EDIT_001:

基于 cmets 并阅读网络上的各种帖子,我决定采用以下方法(见下文)我选择采用共享单例方法,如果需要,我可以选择创建第二个或第三个实例.同样在这个阶段,因为我只将单例用于 MVC 的模型部分,用于一个简单的 iPhone 应用程序,所以我决定将线程安全排除在外。我知道它的重要性,并且随着我对 iPhone 编程的熟悉,我可能会改用 +initialize(记住可以调用两次的子类问题)另外我添加了一个 dealloc,首先在单例时记录一条消息被释放,但也可以在不再需要单例时正确清理。

@interface SharedManager : NSObject
+(id)sharedInstance;
@end

@implementation SharedManager

static id myInstance = nil;

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

-(void)dealloc {
    NSLog(@"_deal: %@", [self class]);
    [super dealloc];
    myInstance = nil;
}
@end

在测试中,我发现我在 dealloc 中将静态变量设置为 nil,或者它保持了指向原始对象的指针。我最初对此有点困惑,因为我期望静态的范围是实例,我猜它是类,这是有道理的。

干杯加里

【问题讨论】:

    标签: iphone objective-c cocoa


    【解决方案1】:

    首先,不要使用此代码。几乎没有理由为一个简单的单身人士做这一切。 Apple 正在展示“强制单例”,因为不可能创建其中两个。真正需要这个是非常罕见的。您几乎总是可以使用大多数具有单例构造函数的 Cocoa 对象所使用的“共享单例”方法。

    这是我实现共享单例的首选方式:

    + (MYManager *)sharedManager
    {
        static MYManager *sharedManager = nil;
        if (sharedManager == nil)
        {
            sharedManager = [[self alloc] init];
        }
        return sharedManager;
    }
    

    就是这样。不需要其他代码。使用+sharedManager 的调用者将获得共享实例。调用+alloc 的调用者可以创建唯一的实例,如果他们真的想要的话。这就是像NSNotificationCenter 这样著名的“单身人士”的工作原理。如果您真的想要自己的私人通知中心,那么班级没有理由禁止它。这种方法具有以下优点:

    • 代码更少。
    • 在非共享实例有用的情况下更加灵活。
    • 最重要的是:代码按照它所说的去做。认为自己正在使用 +alloc 创建一个唯一实例的调用者不会遇到需要他了解对象的内部实现细节的令人惊讶的“远程诡异动作”行为。

    如果您真的需要强制单例,因为有问题的对象映射到无法共享的唯一资源(并且很少遇到这种情况),那么您仍然不应该使用+alloc 强制执行它的诡计。这只是掩盖了尝试创建新实例的编程错误。相反,您应该以这种方式捕获编程错误:

    + (MYManager *)sharedManager
    {
        static MYManager *sharedManager = nil;
        if (sharedManager == nil)
        {
            sharedManager = [[self alloc] initSharedManager];
        }
        return sharedManager;
    }
    
    - (id)init
    {
        NSAssert(NO, @"Attempting to instantiate new instance. Use +sharedManager.");
        return nil;
    }
    
    // Private method. Obviously don't put this in your .h
    - (id)initSharedManager
    {
        self = [super init];
        ....
        return self;
    }
    

    【讨论】:

    • 您可能想要更新此内容以讨论线程安全。 Mike Ash 有一篇关于这方面的好帖子以及其他单身问题:mikeash.com/pyblog/…
    • 更好:读者应该查看 Mike Ash 的帖子。它非常彻底地介绍了如何扩展它以实现线程安全。共享单例上的线程安全问题只会影响初始创建。我很少遇到这种线程问题,因为我通常如何处理线程(在我的代码中,第一次使用单例极不可能在工作线程上)。也就是说,我很想使用 Mike 对 +initialize 的使用,因为它实现起来非常便宜,而且我认为在大多数情况下不太可能出现“过早初始化”问题。
    • @Rob 这仍然不允许多个实例。它只是按照惯例“禁止”它们。 Peter Hosey 写了一篇关于如何更正确地实现单例的精彩文章:boredzo.org/blog/archives/2009-06-17/doing-it-wrong
    • Dave DeLong:+1(显然是 ☺),但我要指出的是,Rob 承认了这一点并提出了反对意见。
    • @Dave,Peter 的文章非常好,我建议人们阅读它,但正如 Peter 指出的那样,我认为覆盖 +allocWithZone: 是不好的,因为它会注入惊喜并掩盖错误(正如 Peter争论并且我同意覆盖保留/释放)。我的最后一个示例确实不允许多个实例超出惯例。如果您想防止忽略警告并调用 -initSharedManager 的调用者,请添加“if (self != theSharedManager)”。我通常认为警告就足够了。如果他们忽视警告,坏事就会发生。不要那样做。他们的程序行不通。
    【解决方案2】:

    在 SO 上有一个很好的例子,说明了使用 cmets 的不同单例方法: What does your Objective-C singleton look like?

    如果有帮助,该示例对 allocWithZone 有不同的方法:返回 nil。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-21
      • 1970-01-01
      • 1970-01-01
      • 2022-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多