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

mat*_*atm 16 singleton objective-c

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

  1. 所以问题:你的Objective-C单身人物是什么样的?
  2. 星期五问答:单身人士的照顾和喂养
  3. Apple文档:创建单例实例

但是这些资源都没有明确地解决init方法概念,而且仍然是Obj-C的新手我很困惑应该如何实现它.

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

我还有什么其他选择?我相信我也应该处理我的单例的子类化方案.

Mec*_*han 25

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

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

+ (id)allocWithZone:(NSZone *)zone
{
    return [[self sharedInstance] retain];
}
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:扩大答案提供示例和更多细节 -

考虑到Catfish_Man的答案,创建一个防弹单体通常并不重要,而只是在你的标题/文档中写一些合理的注释并放入assert.

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

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

//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];
}
Run Code Online (Sandbox Code Playgroud)

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

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

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