ObjectiveC - Singleton
单例可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问从而方便地控制了实例个数,并节约系统资源
使用场合: 在整个应用程序中,共享一份资源 (这份资源只需要创建初始化一次)
ARC 环境下实现单例模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 懒加载的单例的写法
@implementation Demo
// 提供全局变量
static Demo *_instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
return _instance;
}
@end
|
1
2
3
4
5
6
7
| // 使用
Demo *d1 = [[Demo alloc]init];
Demo *d2 = [[Demo alloc]init];
Demo *d3 = [Demo new];
NSLog(@"d1:%p d2:%p d3:%p", d1, d2, d3); // 可见地址都一样
|
使用懒加载的写法,如外界在很多子线程中 alloc,那么会有安全隐患问题
解决多线程下的隐患
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 方法一: 加互斥锁,解决多线程访问安全问题
@implementation Demo
static Demo *_instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
@end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 方法二: GCD 一次性代码,保证整个程序运行中,只执行一次,且线程安全
@implementation Demo
static Demo *_instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
@end
|
优化
- 为了外部方便调用,可像外部提供一个类方法,便于使用单例
- 方法名:
share + 类名
| default + 类名
| 类名
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
| @interface Demo : NSObject
+ (instancetype)shareDemo;
@end
@implementation Demo
static Demo *_instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
// 提供类方法,方便外界访问
+ (instancetype)shareDemo {
// 调用 alloc 时,会调用 allocWithZone
return [[self aloc]init];
}
// 严谨: 由于下面这两个方法也可获取到对象
// 所以也需重写一下,让他们得到的也是单例对象
- (id)copyWithZone:(NSZone *)zone {
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return _instance;
}
@end
|
1
2
3
4
5
6
7
8
9
| // 使用
Demo *d1 = [Demo shareDemo];
Demo *d2 = [Demo new];
Demo *d3 = [Demo shareDemo];
Demo *d4 = [d1 copy];
Demo *d5 = [d1 mutableCopy];
NSLog(@"d1:%p d2:%p d3:%p d4:%p d5:%p", d1, d2, d3, d4, d5); // 可见地址都一样
|
MRC 环境下实现单例模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| @implementation Demo
// 实现同 ARC 单例
#pragma mark - MRC中需要覆盖的方法
// 不需要计数器+1
- (id)retain {
return self;
}
// 不需要,堆区的对象才需要
- (id)autorelease {
return self;
}
// 不需要
- (oneway void)release {}
// 不需要计数器个数, 直接返回最大无符号整数
- (NSUInteger)retainCount {
return UINT_MAX; // 参照常量区字符串的 retainCount
}
@end
|
ARC 与 MRC 通用的单例
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
| @interface Demo : NSObject
+ (instancetype)shareDemo;
@end
@implementation Demo
static Demo *_instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
// 提供类方法,方便外界访问
+ (instancetype)shareDemo {
// 调用 alloc 时,会调用 allocWithZone
return [[self aloc]init];
}
- (id)copyWithZone:(NSZone *)zone {
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return _instance;
}
// 通过宏定义判断是否为 MRC 环境
#pragma mark - MRC 中需要覆盖的方法, ARC与MRC的整合
#if !__has_feature(objc_arc)
- (id)retain {
return self;
}
- (id)autorelease {
return self;
}
- (oneway void)release {}
- (NSUInteger)retainCount {
return UINT_MAX;
}
#endif
@end
|