单例在Swift中的正确实现方式
- 原文链接 :The Right Way to Write a Singleton
- 原文作者 :Hector Matos
- 译文出自 :开发技术前线 www.devtf.cn
- 译者 :Gottabe
尽管在我之前的博文里我就写过关于管理状态的那些坑,但是有时候我们就是无法避免它们。其中一类管理状态的方式我们耳熟能详 - 单例。但是在Swift中有好几种不同的方式来实现一个单例。到底哪一个才是正确的方式呢?在这边博客里,我将和你好好聊聊单例的历史和在Swift中单例正确的实现方式。
如果你想直接就看在Swift中如何正确地写出单例同时看到证明其“正确性”,你可以直接滚动到这篇博文的底部。
让我们先回忆一下
Swfit源于Objective-C,高于Objective-C。在Objective-C中,我们是这样写单例的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@
interface
Kraken
:
NSObject
@
end
@
implementation
Kraken
+
(
instancetype
)
sharedInstance
{
static
Kraken *
sharedInstance
=
nil
;
dispatch_once_t
onceToken
;
dispatch_once
(
&
onceToken
,
^
{
sharedInstance
[
[
Kraken
alloc
]
init
]
;
}
)
;
return
sharedInstance
;
}
@
end
|
当我们把它写出来之后,就能清楚的看到一个单例的架构,接下来我们把单例的规则理一理,以便更好的理解它:
单例的道义
关于单例,有三件事是你必须要记住的:
- 单例必须是唯一的,所以它才被称为单例。在一个应用程序的生命周期里,有且只有一个实例存在。单例的存在给我们提供了一个唯一的全局状态。比如我们熟悉的NSNotification,UIApplication和NSUserDefaults都是单例。
- 为了保持一个单例的唯一性,单例的构造器必须是私有的。这防止其他对象也能创建出单例类的实例。感谢所有帮我指出这点的人。
- 为了确保单例在应用程序的整个生命周期是唯一的,它就必须是线程安全的。当你一想到并发肯定一阵恶心,简单来说,如果你写单例的方式是错误的,就有可能会有两个线程尝试在同一时间初始化同一个单例,这样你就有潜在的风险得到两个不同的单例。这就意味着我们需要用GCD的dispatch_once来确保初始化单例的代码在运行时只执行一次。
成为独一无二并只在一个地方做初始化并不难。这篇博客接下来要记住的内容就是要单例遵守了更难察觉的dispatch_once的规则。
Swift单例
打从Swift 1.0开始,就有好几种创建单例的方法。在这里,这里和这里都有详细的说明。但谁有空点进去看呢?先来个剧透;一共有四种写法,请让我一一道来:
最丑陋的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
TheOneAndOnlyKraken
{
class
var
sharedInstance
:
{
struct
Static
{
static
onceToken
:
dispatch_once_t
=
0
instance
:
TheOneAndOnlyKraken
?
nil
}
dispatch_once
(
&
Static
.
onceToken
)
{
static
.
instance
TheOneAndOnlyKraken
(
)
}
return
Static
.
instance
!
}
}
|
这种写法其实就是把Objective-C中的写法照搬过来。我觉得奇丑无比因为Swift是一门更加简洁且表达力更强的语言。我们要做的比那些搬运工要好,要比他们好。
结构体写法
1
2
3
4
5
6
7
8
9
class
TheOneAndOnlyKraken
{
class
var
sharedInstance
:
{
struct
Static
{
let
instance
TheOneAndOnlyKraken
(
)
}
Static
.
instance
}
}
这个在Swift 1.0的时候必须得这么写,因为那个时候,类还不支持全局类变量。而结构体却支持。正因为在全局变量上的局限性,我们不得不这么写。这比直接把Objective-C那一套搬过来要好,但是还不够。好玩的是,在Swift 1.2发布后的几个月后,我还是经常能看到这些的写法,但这个以后再表。
全局变量写法(又名“一句话写法”)
1
2
3
4
5
6
7
private
let
sharedKraken
TheOneAndOnlyKraken
(
)
class
{
{
sharedInstance
}
}