单例模式怎么定义的,可能在不同的语言,不同的书中不完全一样,但是概况开来都应该是:一个类有且仅有一个实例,并且自行实例化向整个系统提供。
因此,首先你可能需要确定你是真的需要一个单例类,还是说仅仅是需要一个方便调用的实例化方法。如果你是真的需要一个单例类,那么你就应该确保这个单例类,有且仅有一个实例(不管怎么操作都只能获取到这个实例)。
最近看到一些github上的单例使用,别人的用法,有一些思考,然后写demo测试了下,就这个简单的单例也有一些坑呢,希望能给他人一些提醒。
Objective-C中的单例
我们通常在OC中实现一个单例方法都是这样:
1 2 3 4 5 6 7 8 9 |
|
可是这样就可以了么?我做了如下测试:
1 2 3 4 5 6 |
|
看到这个测试,你想到打印结果了么?结果是这样的:
1 2 3 |
|
很明显,通过三种方式创建出来的是不同的实例对象,这就违背了单例类有且仅有一个实例的定义。
为了防止别人不小心利用alloc/init方式创建示例,也为了防止别人故意为之,我们要保证不管用什么方式创建都只能是同一个实例对象,这就得重写另一个方法,实现如下:
1 2 3 4 5 6 7 8 |
|
再次用上面的测试代码,结果是这样的:
1 2 3 |
|
好像用不同的构造方法,获取的都是同一个对象,你以为这样就完了?还早着呢!
一般我们的类里肯定都会有一些属性,然后我就添加了两个property:
1 2 3 |
|
而一些对象类的初始化,或者基础类型的默认值设置都是在init方法里,就像这样:
1 2 3 4 5 6 7 8 9 10 |
|
我重写了HLTestObject类的description方法:
1 2 3 4 5 6 7 8 9 |
|
还是用上面的测试代码,测试结果是这样的:
1 2 3 |
|
可以看到,尽管使用的是同一个示例,可是他们的property值却不一样。
因为尽管没有为示例重新分配内存空间,但是因为又执行了init方法,会导致property被重新初始化。
所以我们需要修改单例的实现。
第一种:
可以将property的初始化或者默认值设置放到dispatch_once 的block内部:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
来看看测试结果:
1 2 3 |
|
第二种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
测试结果:
1 2 3 |
|
注意:
以上代码均是使用ARC的方式管理内存,如果你还在使用MRC(这也太不与时俱进了)。那你还需要重写 retain 和release方法,防止示例引用计数的改变。
Swift中的单例
利用Swift中的一些特性,Swift中的单例可以超级简单,like this:
1 2 3 |
|
可是这样就完了么?同样写一段测试代码:
1 2 3 4 |
|
打印结果却是这样的:
1 2 |
|
所以,我们必须禁用到构造方法:
1 2 3 4 5 |
|
如果有实例属性需要初始化,就可以这样:
1 2 3 4 5 6 7 8 9 10 11 |
|
当然,由于Swift的特性,在Swift中创建单例的方式也不止一种,需要注意的是要确保该类有且仅有一个实例就OK了。