iOS中单例需要注意的

单例模式怎么定义的,可能在不同的语言,不同的书中不完全一样,但是概况开来都应该是:一个类有且仅有一个实例,并且自行实例化向整个系统提供。
因此,首先你可能需要确定你是真的需要一个单例类,还是说仅仅是需要一个方便调用的实例化方法。如果你是真的需要一个单例类,那么你就应该确保这个单例类,有且仅有一个实例(不管怎么操作都只能获取到这个实例)。
最近看到一些github上的单例使用,别人的用法,有一些思考,然后写demo测试了下,就这个简单的单例也有一些坑呢,希望能给他人一些提醒。
Objective-C中的单例
我们通常在OC中实现一个单例方法都是这样:
|
1
2
3
4
5
6
7
8
9
|
static HLTestObject *instance = nil;+ (instancetype)sharedInstance{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[[self class] alloc] init]; }); return instance;} |
可是这样就可以了么?我做了如下测试:
|
1
2
3
4
5
6
|
HLTestObject *objct1 = [HLTestObject sharedInstance];NSLog(@"%@",objct1);HLTestObject *objc2 = [[HLTestObject alloc] init];NSLog(@"%@",objc2);HLTestObject *objc3 = [HLTestObject new];NSLog(@"%@",objc3); |
看到这个测试,你想到打印结果了么?结果是这样的:
|
1
2
3
|
2016-05-23 12:52:57.095 PractiseProject[3579:81998] <hltestobject: 0x7fcf39515510>2016-05-23 12:52:57.095 PractiseProject[3579:81998] <hltestobject: 0x7fcf395c4b70>2016-05-23 12:52:57.095 PractiseProject[3579:81998] <hltestobject: 0x7fcf395c6890></hltestobject: 0x7fcf395c6890></hltestobject: 0x7fcf395c4b70></hltestobject: 0x7fcf39515510> |
很明显,通过三种方式创建出来的是不同的实例对象,这就违背了单例类有且仅有一个实例的定义。
为了防止别人不小心利用alloc/init方式创建示例,也为了防止别人故意为之,我们要保证不管用什么方式创建都只能是同一个实例对象,这就得重写另一个方法,实现如下:
|
1
2
3
4
5
6
7
8
|
+ (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super allocWithZone:zone]; }); return instance;} |
再次用上面的测试代码,结果是这样的:
|
1
2
3
|
2016-05-23 12:57:37.396 PractiseProject[3618:83975] <hltestobject: 0x7f88b9488ac0>2016-05-23 12:57:37.396 PractiseProject[3618:83975] <hltestobject: 0x7f88b9488ac0>2016-05-23 12:57:37.396 PractiseProject[3618:83975] <hltestobject: 0x7f88b9488ac0></hltestobject: 0x7f88b9488ac0></hltestobject: 0x7f88b9488ac0></hltestobject: 0x7f88b9488ac0> |
好像用不同的构造方法,获取的都是同一个对象,你以为这样就完了?还早着呢!
一般我们的类里肯定都会有一些属性,然后我就添加了两个property:
|
1
2
3
|
@property (assign, nonatomic) int height;@property (strong, nonatomic) NSObject *object;@property (strong, nonatomic) NSMutableArray *arrayM; |
而一些对象类的初始化,或者基础类型的默认值设置都是在init方法里,就像这样:
|
1
2
3
4
5
6
7
8
9
10
|
- (instancetype)init{ self = [super init]; if (self) { _height = 10; _object = [[NSObject alloc] init]; _arrayM = [[NSMutableArray alloc] init]; } return self;} |
我重写了HLTestObject类的description方法:
|
1
2
3
4
5
6
7
8
9
|
- (NSString *)description{ NSString *result = @""; result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self]; result = [result stringByAppendingFormat:@" height = %d,",self.height]; result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM]; result = [result stringByAppendingFormat:@" object = %p,",self.object]; return result;} |
还是用上面的测试代码,测试结果是这样的:
|
1
2
3
|
2016-05-23 13:14:43.684 PractiseProject[3781:92758] <hltestobject: 0x7f8a5b458450> height = 20, arrayM = 0x7f8a5b422940, object = 0x7f8a5b4544e0,2016-05-23 13:14:43.684 PractiseProject[3781:92758] <hltestobject: 0x7f8a5b458450> height = 10, arrayM = 0x7f8a5b4552e0, object = 0x7f8a5b45a710,2016-05-23 13:14:43.684 PractiseProject[3781:92758] <hltestobject: 0x7f8a5b458450> height = 10, arrayM = 0x7f8a5b459770, object = 0x7f8a5b4544e0,</hltestobject: 0x7f8a5b458450></hltestobject: 0x7f8a5b458450></hltestobject: 0x7f8a5b458450> |
可以看到,尽管使用的是同一个示例,可是他们的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
|
static HLTestObject *instance = nil;+ (instancetype)sharedInstance{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[[self class] alloc] init]; instance.height = 10; instance.object = [[NSObject alloc] init]; instance.arrayM = [[NSMutableArray alloc] init]; }); return instance;}+ (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super allocWithZone:zone]; }); return instance;}- (NSString *)description{ NSString *result = @""; result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self]; result = [result stringByAppendingFormat:@" height = %d,",self.height]; result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM]; result = [result stringByAppendingFormat:@" object = %p,",self.object]; return result;} |
来看看测试结果:
|
1
2
3
|
2016-05-23 13:29:14.856 PractiseProject[3909:99058] <hltestobject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,2016-05-23 13:29:14.856 PractiseProject[3909:99058] <hltestobject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,2016-05-23 13:29:14.856 PractiseProject[3909:99058] <hltestobject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,</hltestobject: 0x7fa72270c570></hltestobject: 0x7fa72270c570></hltestobject: 0x7fa72270c570> |
第二种:
|
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
|
static HLTestObject *instance = nil;+ (instancetype)sharedInstance{ return [[self alloc] init];}- (instancetype)init{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super init]; instance.height = 10; instance.object = [[NSObject alloc] init]; instance.arrayM = [[NSMutableArray alloc] init]; }); return instance;}+ (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super allocWithZone:zone]; }); return instance;}- (NSString *)description{ NSString *result = @""; result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self]; result = [result stringByAppendingFormat:@" height = %d,",self.height]; result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM]; result = [result stringByAppendingFormat:@" object = %p,",self.object]; return result;} |
测试结果:
|
1
2
3
|
2016-05-23 13:31:44.824 PractiseProject[3939:100662] <hltestobject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,2016-05-23 13:31:44.825 PractiseProject[3939:100662] <hltestobject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,2016-05-23 13:31:44.825 PractiseProject[3939:100662] <hltestobject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,</hltestobject: 0x7fa9da711a70></hltestobject: 0x7fa9da711a70></hltestobject: 0x7fa9da711a70> |
注意:
以上代码均是使用ARC的方式管理内存,如果你还在使用MRC(这也太不与时俱进了)。那你还需要重写 retain 和release方法,防止示例引用计数的改变。
Swift中的单例
利用Swift中的一些特性,Swift中的单例可以超级简单,like this:
|
1
2
3
|
class HLTestObject: NSObject { static let sharedInstance = HLTestObject();} |
可是这样就完了么?同样写一段测试代码:
|
1
2
3
4
|
let object1 = HLTestObject.sharedInstance;print(object1);let object2 = HLTestObject();print(object2); |
打印结果却是这样的:
|
1
2
|
<swiftproject.hltestobject: 0x7f90ebc74e50><swiftproject.hltestobject: 0x7f90ebe5cf40></swiftproject.hltestobject: 0x7f90ebe5cf40></swiftproject.hltestobject: 0x7f90ebc74e50> |
所以,我们必须禁用到构造方法:
|
1
2
3
4
5
|
class HLTestObject: NSObject { static let sharedInstance = HLTestObject(); private override init() { }} |
如果有实例属性需要初始化,就可以这样:
|
1
2
3
4
5
6
7
8
9
10
11
|
class HLTestObject: NSObject { var height = 10; var arrayM: NSMutableArray var object: NSObject static let sharedInstance = HLTestObject(); private override init() { object = NSObject() arrayM = NSMutableArray() super.init() }} |
当然,由于Swift的特性,在Swift中创建单例的方式也不止一种,需要注意的是要确保该类有且仅有一个实例就OK了。
iOS中单例需要注意的的更多相关文章
- IOS中单例NSUserDefaults的使用(转)
一.了解NSUserDefaults以及它可以直接存储的类型 http://my.oschina.net/u/1245365/blog/294449 NSUserDefaults是一个单例,在整个程序 ...
- iOS中单例创建时不严格造成的问题和解决方法
这次项目中遇到了一个单例创建不严格造成了的问题.简单说来就是在有的地方使用了alloc创建了多个实例,当然如果严格按照接口的方法调用是不会有问题的,但是如果项目碰到有不太熟悉的人使用时在处理时就会出现 ...
- 【iOS 单例设计模式】底层解析与运用
[iOS 单例设计模式]底层解析与运用 一.单例设计名词解释: (官方解释)单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例.(形象比喻)程序 — 公司 单例实例 - 管理 ...
- swift 中单例的写法
在swift中单例的写法和oc的有所不同,在书写的时候又分很多种写法,,如果一个.swift 文件只创建了一个类,可以用那种dispatch_once的写法,如果一个.swift文件中有很多类的存在, ...
- ios 单例的再次理解
单例模式 在建模的时候,如果这个东西确实只需要一个对象,多余的对象都是无意义的,那么就考虑用单例模式.比如定位管理(CLLocationManager),硬件设备就只有一 个,弄再多的逻辑对象 ...
- iOS单例创建的一点疑惑
线程安全的单例常用写法, +(AccountManager *)sharedManager{ static AccountManager *defaultManager = nil; disptch_ ...
- beanfactory中单例bean的初始化过程(一)
Date 10.06 pm Point 完成beanfactory中单例bean的初始化 beanFactory.preInstantiateSingletons() 拿到所有的bean定义信息(在 ...
- iOS 单例传值遇见问题
单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模式的要点有三个:一是某个类只能有一个实例: ...
- iOS单例的两种实现
单例模式算是开发中比较常见的一种模式了.在iOS中,单例有两种实现方式(至少我目前只发现两种).根据线程安全的实现来区分,一种是使用@synchronized,另一种是使用GCD的dispatch_o ...
随机推荐
- 【★】深入BGP原理和思想【第…
前言:学思科技术我想说,浅尝辄止,不是天才千万别深钻.和我研究高等数学一样,越深入就会发现越多的问题与不合理之处.尤其对于IT界,算法的最终解释权还是掌握在老外手中,所以对于有些细节,我们" ...
- 转: 【Java并发编程】之十八:第五篇中volatile意外问题的正确分析解答(含代码)
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17382679 在<Java并发编程学习笔记之五:volatile变量修饰符-意料之外 ...
- 第二次作业——个人项目实战(Sudoku)
Github:Sudoku 项目相关要求 利用程序随机构造出N个已解答的数独棋盘 . 输入 数独棋盘题目个数N 输出 随机生成N个 不重复 的 已解答完毕的 数独棋盘,并输出到sudoku.txt中, ...
- 201521123012 《Java程序设计》第七周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 参考资料: XMind 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代 ...
- Servlet一些基础
Servlet 是一套规范,规定了如何通过Java代码来开发动态网站,并由 javax.servlet 和 javax.servlet.http 两个包中的类来实现. servlet是一个服务器端组建 ...
- JavaSE(十)之Map总结
上一篇是总结了Collection接口的各种实现类,这一篇我将分享的是Map的总结,希望大家点评! 一.Map接口 1.1.为什么Collection不能满足集合的所有功能? Collection接口 ...
- java.lang.IllegalArgumentException: object is not an instance of declaring class
今天在使用反射的时候,出现了java.lang.IllegalArgumentException: object is not an instance of declaring class错误-具体是 ...
- JSP第四篇【EL表达式介绍、获取各类数据、11个内置对象、执行运算、回显数据、自定义函数、fn方法库】
什么是EL表达式? 表达式语言(Expression Language,EL),EL表达式是用"${}"括起来的脚本,用来更方便的读取对象! EL表达式主要用来读取数据,进行内容的 ...
- 《Java从入门到放弃》JavaSE入门篇:网络编程(入门版)
要进行网络编程,首先要搞清楚目的是什么. 网络编程说简单点就是在网络上的计算机进行数据的交互. 既然要进行数据交互,那就需要有一个发送方和一个接受方. 按网络上的说法,就是一个攻一个受· 当然,现阶段 ...
- cocos2dx 播放gif
起因 或许有人会说,cocos2dx中直接帧动画就行了用什么GIF. 起因是为游戏内部要用到第三方平台的头像,而第三方平台的头像大多都是用到Gif,所以才会有了这个需求 过程 查了各种文档都没找到.但 ...