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

Objective-C singleton应该如何实现init方法?,objective-c,singleton,Objective C,Singleton,我在Obj-C中阅读了几篇关于单身的精彩参考资料: 因此,问题: 周五问答: 苹果文档: 但是这些资源中没有一个明确地解决了init方法概念,虽然我还是Obj-C的新手,但我不知道应该如何实现它 到目前为止,我知道在Obj-C中使用initprivate是不可能的,因为它不提供真正的私有方法。。。因此,用户可以调用[[MyClass alloc]init]而不是使用my[MyClass sharedInstance] 我的其他选择是什么?我认为我还应该处理我的singleton的子类场景。不应该

我在Obj-C中阅读了几篇关于单身的精彩参考资料:

  • 因此,问题:
  • 周五问答:
  • 苹果文档:
  • 但是这些资源中没有一个明确地解决了
    init
    方法概念,虽然我还是Obj-C的新手,但我不知道应该如何实现它

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


    我的其他选择是什么?我认为我还应该处理我的singleton的子类场景。

    不应该影响
    init
    方法。它在单例类中与在常规类中相同。您可能希望重写
    allocWithZone:
    (由
    alloc
    调用)以避免创建多个类实例。

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

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

    + (id)allocWithZone:(NSZone *)zone
    {
        return [[self sharedInstance] retain];
    }
    
    这意味着用户仍将获得您的singleton实例,并且他们可能会错误地使用它,就好像他们分配了它一样,并安全地释放它一次,因为此自定义alloc对singleton执行保留操作。(注意:
    alloc
    调用
    allocWithZone:
    并且不需要单独重写。)

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

    编辑:展开答案以提供示例和更多详细信息--

    考虑到Catfish_-Man的回答,创建一个防弹单例通常并不重要,相反,只需在标题/文档中编写一些合理的注释,并在
    断言中添加

    然而,在我的例子中,我想要一个线程安全的延迟加载单例——也就是说,它在需要使用之前不会被分配,而不是在应用程序启动时自动分配。在学习了如何安全地完成这项工作后,我想我还是一直这样做为好

    EDIT#2:我现在使用GCD的
    dispatch_once(…)
    作为线程安全方法,在应用程序的生命周期内只分配一次单例对象。看见我还添加了苹果旧的singleton示例中的
    allocWithZone:
    override位,并添加了一个名为
    SingleTonIt
    的私有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
    中保留默认实现,根据苹果的文档,它只返回self。这意味着
    [[HSCloudManager alloc]init]
    [[[HSCloud Manager sharedManager]retain]self]
    相同,这使得混乱的用户和多线程应用程序都可以安全地作为延迟加载单例

    至于您对用户将您的单例子类化的担忧,我想说的是,请清楚地评论/记录它。任何一个在没有阅读课堂内容的情况下盲目地子类化的人都是在自讨苦吃


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

    老实说?在我看来,编写防弹单例类的整个时尚似乎有些言过其实。如果您非常关心它,那么在第一次分配它之前,只需将assert(sharedInstance==nil)放在那里。这样,如果有人使用错误,它将崩溃,并立即让他们知道自己是白痴。

    要使init/new方法对singleton类的调用者不可用,可以在头文件中使用NS_unavailable宏:

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

    @雅克,看起来很公平,但我觉得自己好像在无限循环中,需要消化;)如何确保通过私有方法创建此类单例的过程是线程安全的?其他问题:如果我遵循苹果的
    allocWithZone
    理念,将调用默认NSObject的
    init
    (是否只是返回
    self
    或做其他事情?)。。。然后,用户再次尝试使用
    alloc
    init
    进行实例化,这是否会再次改变我的属性/ivars初始化和NSObject获取
    init
    的任何内容?雅克,你说得对:你现在建议的合理方法就足够了。我将使用你的代码更多地处理单身汉:)我认为你的答案相当广泛,并且考虑了鲶鱼的反对意见,所以我接受它。再次感谢!我建议分派_once()而不是OSAtomic*。不那么挑剔,一样快或更快。漂亮的图案!谢谢。如果我们调用[alloc]init],init方法将被调用两次(一次使用sharedManager方法实现,另一次使用init-itslef)?可以吗?。。。基本上我看到的是[[[super-allocWithZone:NULL]init]init];我部分同意,但另一半说:为什么要妥协?不过,我会尽我所能,在标题/文档中添加清晰的注释。