很好,一个绕过
init
的简单方法是不编写
init
方法,这样它就会调用默认的NSObject实现(该实现仅返回
self
)。然后,对于您的
sharedInstance
函数,在实例化单例时定义和调用执行类似init的工作的私有函数。(这避免了用户意外重新初始化您的单例。)
但是!!!主要问题出现在代码用户调用
alloc
时!对此,我个人建议沿用苹果覆盖
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,以防止它被意外调用多次:
-(void)singletonInit
{
}
static HSCloudManager * sharedInstance = nil;
+ (HSCloudManager *) sharedManager {
static dispatch_once_t dispatchOncePredicate = 0;
dispatch_once(&dispatchOncePredicate, ^{
sharedInstance = [[super allocWithZone:NULL] init];
[sharedInstance singletonInit];
});
return sharedInstance;
}
+ (id) allocWithZone:(NSZone *)zone {
return [[HSCloudManager sharedManager] retain];
}
HSCloudManager
是 NSObject
的子类,没有重写 init
方法,因此只有 NSObject
中默认的实现,根据苹果文档,其只返回 self。这意味着 [[HSCloudManager alloc] init]
和 [[[HSCloud Manager sharedManager] retain] self]
是相同的,因此它是一个安全的懒加载单例,适用于混淆用户和多线程应用程序。
至于您对用户子类化单例的担忧,我建议清晰地进行注释/文档说明。任何盲目继承而不阅读类信息的人都会遭受痛苦!
编辑#3: 为了 ARC 兼容性,只需从 allocWithZone:
重写中删除保留部分,但保留重写。
allocWithZone
的想法,默认的 NSObject 的init
将被调用(只是返回self
还是做其他事情?)...然后再次尝试使用alloc
和init
实例化,这会对我的属性/实例变量初始化和 NSObject 再次获取init
有影响吗? - matminit
方法,就是安全的。NSObject 的init
是空的。(这就是为什么我建议使用私有的+[singletonInit]
方法。)但是,尽管如此是安全的,如果您知道它是一个单例,您也不应该调用[[.. alloc] init]
-- 而应该使用它的+[sharedInstance]
方法。 - MechEthan