Objective-C singleton应该如何实现init方法?

时间:2011-09-01 17:52:08

标签: objective-c singleton

我在Obj-C中阅读了一些关于单身人士的惊人资源:

  1. 问题:What does your Objective-C singleton look like?
  2. 星期五Q& A:Care and Feeding of Singletons
  3. Apple文档:Creating a Singleton Instance
  4. 但是这些资源都没有解决init方法概念显式,而且仍然是Obj-C的新手我很困惑应该如何实现它。

    到目前为止,我知道在Obj-C中无法使用init私有,因为它不提供真正的私有方法...所以用户可以调用[[MyClass alloc] init]而不是使用我的{ {1}}。

    我的其他选择是什么?我相信我也应该处理我的单身的子类化方案。

4 个答案:

答案 0 :(得分:25)

嗯,围绕init的一个简单方法就是不要写一个让它调用默认的NSObject实现(只返回self)。然后,对于sharedInstance函数,定义并调用一个私有函数,在实例化单例时执行类似init的工作。 (这可以避免用户意外重新初始化您的单身人士。)

然而!!! 主要问题是代码用户调用了alloc!为此,我个人推荐Apple的覆盖allocWithZone: ...

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

这意味着用户仍然会获得您的单例实例,并且他们可能会错误地使用它们,就像分配它一样,并且安全地释放它一次,因为此自定义alloc对单例执行保留。 (注意:alloc调用allocWithZone:,无需单独覆盖。)

希望有所帮助!如果您想了解更多信息,请告诉我们

编辑:扩展答案以提供示例和更多详情 -

考虑到Catfish_Man的答案, 通常对于创建一个防弹单身并不重要,而只是在你的标题/文档中写下一些合理的评论并放入assert

但是,在我的情况下,我想要一个线程安全的延迟加载单例 - 也就是说,在需要使用它之前它不会被分配,而不是在应用程序启动时自动分配。在学会了如何安全地做到这一点之后,我想我也可以一路走下去。

编辑#2:我现在使用GCD的dispatch_once(...)来实现一种线程安全的方法,即在应用程序的生命周期内只分配一个单例对象。见Apple Docs: GCD dispatch_once。我还在Apple的旧单例示例中添加了allocWithZone:覆盖位,并添加了一个名为singletonInit的私有init,以防止意外被多次调用:

//Hidden/Private initialization
-(void)singletonInit 
{
   //your init code goes here
}

static HSCloudManager * sharedInstance = nil;   

+ (HSCloudManager *) sharedManager {                                   
    static dispatch_once_t dispatchOncePredicate = 0;                  
    dispatch_once(&dispatchOncePredicate, ^{                           
        sharedInstance = [[super allocWithZone:NULL] init];          
        [sharedInstance singletonInit];//Only place you should call singletonInit 
    });                                                                
    return sharedInstance;                                                       
}

+ (id) allocWithZone:(NSZone *)zone {
    //If coder misunderstands this is a singleton, behave properly with  
    // ref count +1 on alloc anyway, and still return singleton!
    return [[HSCloudManager sharedManager] retain];
}

HSCloudManager子类NSObject,并且不会覆盖init,只留下NSObject中的默认实现,根据Apple的文档,它只返回self。这意味着[[HSCloudManager alloc] init][[[HSCloud Manager sharedManager] retain] self]相同,使得混淆用户和多线程应用程序都可以安全地作为延迟加载单例。

至于你对用户子类化单子的关注,我会说清楚地评论/记录它。任何盲人继承而没有在课堂上阅读的人都要求痛苦!

编辑#3:对于 ARC兼容性,只需从allocWithZone:覆盖中删除保留部分,但保留覆盖。

答案 1 :(得分:3)

诚实?编写防弹单身人士课程的整个时尚对我来说似乎相当夸张。如果你真的很关心它,那么在你第一次分配它之前,先将断言(sharedInstance == nil)放在那里。这样一来,如果有人误用它会立即让他们知道他们是个白痴,就会崩溃。

答案 2 :(得分:2)

init方法不应受到影响。在单例类中与在常规类中相同。您可能希望覆盖allocWithZone:(由alloc调用),以避免创建多个类的实例。

答案 3 :(得分:0)

要使单例类的调用者无法使用init / new方法,可以在头文件中使用NS_UNAVAILABLE宏:

- (id)init NS_UNAVAILABLE; 
+ (id)new NS_UNAVAILABLE; 

+ (instancetype)sharedInstance;