好吧,解决init 的一种简单方法是不写一个让它调用默认的NSObject 实现(它只返回self)。然后,对于您的sharedInstance 函数,定义并调用一个私有函数,该函数在您实例化单例时执行类似初始化的工作。 (这样可以避免用户意外地重新初始化你的单例。)
但是!!!主要问题是alloc 被您的代码的用户调用!为此,我个人推荐苹果的路线覆盖allocWithZone: ...
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
这意味着用户仍然会得到你的单例实例,他们可能会错误地使用它,就好像他们分配了它一样,并安全地释放它一次,因为这个自定义分配对单例执行保留。 (注:alloc 调用allocWithZone:,不需要单独覆盖。)
希望对您有所帮助!如果您想了解更多信息,请告诉我~
编辑:扩展答案以提供示例和更多详细信息 --
考虑到 Catfish_Man 的回答,通常创建一个防弹单例并不重要,而只需在您的标题/文档中编写一些合理的 cmets 并放入 assert。
但是,就我而言,我想要一个线程安全的延迟加载单例——也就是说,它在需要使用之前不会被分配,而不是在应用启动时自动分配。在学习了如何安全地做到这一点之后,我想我不妨一路走下去。
EDIT#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] 相同,使其对于困惑的用户和多线程应用程序作为延迟加载单例是安全的。
至于您对用户子类化您的单身人士的担忧,我想说清楚地评论/记录它。任何盲目的子类化而不阅读课程的人都是要求痛苦!
EDIT#3:为了 ARC 兼容性,只需从 allocWithZone: 覆盖中删除保留部分,但保留覆盖。