因为单例是全局哪里要用直接调用就行非常方便简单,一般我们可以用单例来作对用户信息的存储,其次单例可以做成购物车之类的页面等等。当然单例最大的优势个人感觉就是对数据的存储和读取非常方便,就可以解决页面之间传值困难的问题。简单讲下怎样用单例对数据传输吧,把需要的数据都定义成属性,当需要存储的时候直接调用存储就行,要用的时候把它调出使用就行了这里不做过多描述了。
单例模式是一个类在系统中只有一个实例对象。通过全局的一个入口点对这个实例对象进行访问。在iOS开发中,单例模式是非常有用的一种设计模式。如下图,是一个简单的例模式的UML类图。
单例模式是一个类在系统中只有一个实例对象。通过全局的一个入口点对这个实例对象进行访问。在iOS开发中,单例模式是非常有用的一种设计模式。如下图,是一个简单的例模式的UML类图。
iOS SDK中也有许多类使用了单例模式,例如,UIApplication:当程序启动的时候,会调用UIApplicationMain方法,在该方法中,会实例化一个UIApplication对象,之后在程序中的任意地方调用sharedApplication方法都将返回一个与当前应用程序相关的UIApplication实例(UIApplicationMain方法中创建的UIApplication单例)。
什么时候使用单例模式?
在程序中,单例模式经常用于只希望一个类只有一个实例,而不运行一个类还有两个以上的实例。当然,在iOS SDK中,根据特定的需求,有些类不仅提供了单例访问的接口,还为开发者提供了实例化一个新的对象接口,例如,NSFileManager可以通过defaultManager方法返回相同的一个NSFileManager对象。如果需要新的一个NSFileManager实例对象,可以通过init方法。
iOS中单例模式的实现
iOS中单例模式的实现方式一般分为两种:Non-ARC(非ARC)和ARC+GCD。
1.Non-ARC(非ARC)
非ARC的实现方法如下所示:
BVNonARCSingleton.h
1.//
2.// BVNonARCSingleton.h
3.// SingletonPattern
4.//
5.// Created by BeyondVincent on 13-5-9.
6.// Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8.
9.#import <Foundation/Foundation.h>
10.
11.@interface BVNonARCSingleton : NSObject
12.
13.@property ( nonatomic, retain) NSString *tempProperty;
14.+ (BVNonARCSingleton *)sharedInstance;
15.
16.@end11.@implementation BVNonARCSingleton
12.
13.static BVNonARCSingleton *sharedInstance = nil;
14.
15.// 获取一个sharedInstance实例,如果有必要的话,实例化一个
16.+ (BVNonARCSingleton *)sharedInstance {
17. if (sharedInstance == nil) {
18. sharedInstance = [[super allocWithZone:NULL] init];
19. }
20.
21. return sharedInstance;
22.}
23.
24.// 当第一次使用这个单例时,会调用这个init方法。
25.- (id)init
26.{
27. self = [super init];
28.
29. if (self) {
30. // 通常在这里做一些相关的初始化任务
31. }
32.
33. return self;
34.}
35.
36.// 这个dealloc方法永远都不会被调用--因为在程序的生命周期内容,该单例一直都存在。(所以该方法可以不用实现)
37.-(void)dealloc
38.{
39. [super dealloc];
40.}
41.
42.// 通过返回当前的sharedInstance实例,就能防止实例化一个新的对象。
43.+ (id)allocWithZone:(NSZone*)zone {
44. return [[self sharedInstance] retain];
45.}
46.
47.// 同样,不希望生成单例的多个拷贝。
48.- (id)copyWithZone:(NSZone *)zone {
49. return self;
50.}
51.
52.// 什么也不做——该单例并不需要一个引用计数(retain counter)
53.- (id)retain {
54. return self;
55.}
56.
57.// 替换掉引用计数——这样就永远都不会release这个单例。
58.- (NSUInteger)retainCount {
59. return NSUIntegerMax;
60.}
61.
62.// 该方法是空的——不希望用户release掉这个对象。
63.- (oneway void)release {
64.
65.}
66.
67.//除了返回单例外,什么也不做。
68.- (id)autorelease {
69. return self;
70.}
71.
72.@end2.@synchronized (self)
3.{
4. if(sharedInstance == nil)
5. {
6. sharedInstance = [[super allocWithZone:NULL] init];
7. }
8.}11.@interface BVARCSingleton : NSObject
12.
13.@property ( nonatomic, weak) NSString *tempProperty;
14.+ (BVARCSingleton *)sharedInstance;
15.
16.@end11.@implementation BVARCSingleton
12.
13.+ (BVARCSingleton *) sharedInstance
14.{
15. static BVARCSingleton *sharedInstance = nil ;
16. static dispatch_once_t onceToken; // 锁
17. dispatch_once (& onceToken, ^ { // 最多调用一次
18. sharedInstance = [[self alloc] init];
19. });
20. return sharedInstance;
21.}
22.
23.// 当第一次使用这个单例时,会调用这个init方法。
24.- (id)init
25.{
26. self = [super init];
27.
28. if (self) {
29. // 通常在这里做一些相关的初始化任务
30. }
31.
32. return self;
33.}
34.
35.@end2.@synchronized (self)
BVNonARCSingleton.m
1.//
2.// BVNonARCSingleton.m
3.// SingletonPattern
4.//
5.// Created by BeyondVincent on 13-5-9.
6.// Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8.
9.#import "BVNonARCSingleton.h"
10.
实际上上面的代码苹果官网也有提供:Creating a Singleton Instance,只不过没有给出头文件的定义。上面用非ARC实现单例的方法是线程不安全的,如果有多个线程同时调用sharedInstance方法获取一个实例,而sharedInstance方法需要花费1-2秒钟的时间,那么BVNonARCSingleton的init方法就可能会被多次调用,也就是不同线程获得的BVNonARCSingleton有可能不是同一个实例。怎么解决线程的不安全呢?答案是使用@synchronized来创建互斥锁即可。
1.// 保证在实例化的时候是线程安全的(当然,该方法不能保证该单例中所有方法的调用都是线程安全的)
2.@synchronized (self)
3.{
4. if(sharedInstance == nil)
5. {
6. sharedInstance = [[super allocWithZone:NULL] init];
7. }
8.}
通过上面的代码就能保存线程安全。
1.<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">提醒:在iOS中,一般不建议使用非ARC来实现单例模式。更好的方法是使用ARC+GCD来实现。</span>
2.ARC+GCD
通过ARC+GCD的方法来实现单例模式的非常简单的。下面先来看看具体实现:
BVARCSingleton.h
1.//
2.// BVARCSingleton.h
3.// SingletonPattern
4.//
5.// Created by BeyondVincent on 13-5-9.
6.// Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8.
9.#import <Foundation/Foundation.h>
10.
BVARCSingleton.m
1.//
2.// BVARCSingleton.m
3.// SingletonPattern
4.//
5.// Created by BeyondVincent on 13-5-9.
6.// Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8.
9.#import "BVARCSingleton.h"
10.
在上面的代码中,调用Grand Central Dispatch (GCD)中的dispatch_once方法就可以确保BVARCSingleton只被实例化一次。并且该方法是线程安全的,我们不用担心在不同的线程中,会获得不同的实例。(当然,该方法同样不能保证该单例中所有方法的调用都是线程安全的)。
当然,在ARC中,不用GCD也是可以做到线程安全的,跟之前非ARC代码中使用@synchronized一样,如下代码:
1. // 不使用GCD,通过@synchronized
2.@synchronized (self)
3.{
4. if(sharedInstance == nil)
5. {
6. sharedInstance = [[self alloc] init];
7. }
8.}
为了简化使用ARC+GCD来创建单例,可以定义下面这样的一个宏:
1.#define DEFINE_SHARED_INSTANCE_USING_BLOCK(block)
2.static dispatch_once_t onceToken = 0;
3.__strong static id sharedInstance = nil;
4.dispatch_once(&onceToken, ^{
5.sharedInstance = block();
6.});
7.return sharedInstance;
实例化的实现方法如下所示:
1.+ (BVARCSingleton *) sharedInstance
2.{
3. DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
4. return [[self alloc] init];
5. });
6.}
单例的使用
单例的使用方法很简单,在代码中的任意位置,如下使用即可:
在BVAppDelegate.m中添加头文件:
1.#import "BVNonARCSingleton.h"
2.#import "BVARCSingleton.h"
如下使用方法:
1.- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2.{
3. [BVNonARCSingleton sharedInstance].tempProperty = @"非ARC单例的实现";
4. NSLog(@"%@", [BVNonARCSingleton sharedInstance].tempProperty);
5.
6. [BVARCSingleton sharedInstance].tempProperty = @"ARC单例的实现";
7. NSLog(@"%@", [BVARCSingleton sharedInstance].tempProperty);
8.
9. return YES;
10.}
运行程序,会在控制台窗口输出如下内容:
1.2013-05-09 16:44:07.649 SingletonPattern[5159:c07] 非ARC单例的实现
2.2013-05-09 16:44:33.204 SingletonPattern[5159:c07] ARC单例的实现
单例模式算是开发中比较常见的一种模式了。在iOS中,单例有两种实现方式(至少我目前只发现两种)。根据线程安全的实现来区分,一种是使用@synchronized,另一种是使用GCD的dispatch_once函数。
要实现单例,首先需要一个static的指向类本身的对象,其次需要一个初始化类函数。下面是两种实现的代码。
1、@synchronized
static InstanceClass *instance;
+ (InstanceClass *)defaultInstance{
@synchronized (self){
if (instance == nil) {
instance = [[InstanceClass alloc] init];
}
}
return instance;
}
2、GCD
static InstanceClass *instance;
+ (InstanceClass *)defaultInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[InstanceClass alloc] init];
});
return instance;
}
总的来说,两种实现效果相同,但第二种GCD的实现方式写起来比较简单。如果不习惯GCD的方式,可以使用第一种方式。
在软件工程中,单例是一种用于实现单例的数学概念,即将类的实例化限制成仅一个对象的设计模式。
单例是一种类,该类只能实例化一个对象。
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
如果被多个线程调用,该函数会同步等等直至代码块完成。
+ (AccountManager *)sharedManager {
static AccountManager *sharedAccountManagerInstance = nil;
static dispatch_once_t predicate; dispatch_once(&predicate, ^{
sharedAccountManagerInstance = [[self alloc] init];
});
return sharedAccountManagerInstance;
}
AccountManager *accountManager = [AccountManager sharedManager];
AccountManager *accountManager = [[AccountManager alloc] init];
单例模式介绍
单例模式确保在一个应用中只产生一个实例,这是很有必要的,因为在我们做软件设计的时候,有很多对象都是只需要一个就可以了,而不需要创建众多的对象,这样最显而易见的就是节省了内存空间。而且避免了这个类的频繁的初始化与销毁。有时为了实现某一种功能与操作而创建的类(工具类)往往也不需要多个对象,使用单例模式再合适不过。再延伸一点,有时为了节省内存对一个对象进行复用的话也可以通过单例来实现,这在手机软件的开发中用得比较多,因为手机的内存实在是少得可怜。
单例模式优点
- 正如前面说的,单例模式在内存中只有一个实例,减少了内存开支。特别是一个对象需要频繁的创建、销毁时,而创建与销毁的性能有无法优化,单例模式的优势就非常明显。
- 单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
- 单例模式可以避免对资源的多重占用。
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问。
单例模式缺点
- 单例模式一般没有接口,扩展很困难,除了修改代码基本上没有第二种途径实现。
- 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的。
- 单例模式与单一职责原则有冲突。
单例模式在iOS中的使用
单例模式在iOS
开发中的使用还是蛮多的,许多Foundation
、Cocoa
和UIKit
中的类都实现了单例模式,比如应用程序本身UIApplication
、文件操作类NSFileManager
、消息中心NSNotificitonCenter
等系统都已经给我们实现单例,我们只需要使用就好了。在iOS
中使用单例模式要使用类方法,通过类方法返回该类的唯一对象。
我知道的在iOS
开发中实现单例模式主要有以下三种方式:
第一种
该方法是苹果的官方文档中写的一种方式,通过覆盖NSObject
的部分方法实现,使该类无法alloc
、retain
、release
。这是最麻烦的一种方法,也是最不好的一种方法。
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
34
35
36
37
38
39
|
|
可以看到这种方式,使用静态成员维持了一个永久存在的对象,而且覆盖了alloc
方法(alloc
方法会调用allocWithZone:
方法),并且也覆盖了所有与引用技术有关的方法,这都使这个对象不会被销毁。这样看上去基本实现了我们需要的,但是写起来麻烦不说,还有很大的一个问题,那就是多线程问题,如果是在多线程中那么该种方法就不能保证只产生一个对象了。所以这种方式只是介绍一下,并不推荐使用。
第二种
第二种跟第一种差不多,也是通过覆盖NSObject
的方法实现的,但是它在第一种的基础上增加了多线程的处理,所以即使在多线程下,该种方法创建的对象也是唯一的。这种方法已经有大牛为我们写好了,全都都是通过C
的宏定义#define
出来了。现给出该头文件:
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|