我在Obj-C中阅读了一些关于单身人士的惊人资源:
但是这些资源都没有解决init
方法概念显式,而且仍然是Obj-C的新手我很困惑应该如何实现它。
到目前为止,我知道在Obj-C中无法使用init
私有,因为它不提供真正的私有方法...所以用户可以调用[[MyClass alloc] init]
而不是使用我的{ {1}}。
我的其他选择是什么?我相信我也应该处理我的单身的子类化方案。
答案 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;