iOS 设计模式之四(单例模式)
一、概念
1、单例模式的动机
实际开发中,为了节约系统资源,并且共享一份数据,有时需要确保系统中某个类只有唯一一个实例,当这个唯一实例创建成功之后,我们无法再创建一个同类型的其他对象,所有的操作都只能基于这个唯一实例。为了确保对象的唯一性,我们可以通过单例模式来实现,这就是单例模式的动机所在。
2、单例模式的定义
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
单例模式有三个要点:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
3、饿汉式单例与懒汉式单例
饿汉式单例:当类被系统加载时,静态变量instance会被初始化,单例类的唯一实例将被创建。
懒汉式单例:在类第一次调用的时候,instance被初始化,单例类的唯一实例将被创建。
4、结构图
这里只放一个普遍的单例模式结构图,饿汉式单例与懒汉式单例区别就是静态变量instance初始化的时间不同。
单例模式二、示例
1、饿汉式单例代码
根据饿汉式单例的定义,在类被加载的时候就对静态instance初始化,Java代码比较简单:
Class Singleton {
private static final Singleton instance = new Singleton(); //保证实例唯一
private Singleton() { } //私有初始化方法
public static Singleton getInstance() { //利用工厂模式统一提供对外方法
return instance;
}
}
在iOS开发中,饿汉式单例代码略显奇怪:
// .h文件
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
@end
// .m文件
static Singleton *instance; //声明静态变量
@implementation Singleton
+ (void)load { // 在类被加载的时候会调用一次load方法,这个时候进行初始化
instance = [[super allocWithZone:NULL] init];
}
+ (instancetype)sharedInstance { // 统一对外方法
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone { //就算通过alloc创建的也是同一个instance
return instance;
}
@end
2、懒汉式单例代码
根据懒汉式单例定义,在类第一次调用的时候对静态instance进行初始化,Java代码简单:
class LazySingleton {
private static LazySingleton instance = null; //加载的时候不初始化
private LazySingleton() { } //私有初始化方法
public static LazySingleton getInstance() { //第一次调用初始化,synchronized进行线程同步
if (instance == null) {
synchronized (LazySingleton.class) {
instance = new LazySingleton();
}
}
return instance;
}
}
在iOS开发中,懒汉式单例代码如下:
// .h文件
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
@end
// .m文件
@implementation Singleton
+ (instancetype)sharedInstance {
return [[self alloc] init]; //alloc最终会调用allocWithZone方法
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static Singleton *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ //dispatch_once里只会调用一次,是线程安全的
instance = [[super allocWithZone:NULL] init];
});
return instance;
}
@end
运行代码:
- (void)viewDidLoad {
[super viewDidLoad];
Singleton *s1 = [Singleton sharedInstance];
Singleton *s2 = [[Singleton alloc] init];
NSLog(@"s1 = %p", s1);
NSLog(@"s2 = %p", s2);
}
打印结果:s1与s2打印地址是一样的,是同一个实例。
s1 = 0x60000380c060
s2 = 0x60000380c060
3、iOS项目中常见用法
在iOS实际开发中,经常是使用另外一种方式创建单例模式,严格来说这种做法不严谨,但是代码简单方便,所以使用还是广泛。
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
@end
@implementation Singleton
+ (instancetype)sharedInstance {
static Singleton *instance; //声明静态变量
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ //第一次调用的时候初始化一次
instance = [Singleton new];
});
return instance; //返回静态变量
}
@end
这种做法,如果通过alloc创建的实例会不一样。
运行代码:
- (void)viewDidLoad {
[super viewDidLoad];
Singleton *s1 = [Singleton sharedInstance];
Singleton *s2 = [[Singleton alloc] init];
NSLog(@"s1 = %p", s1);
NSLog(@"s2 = %p", s2);
}
打印结果:s1和s2指针地址不同,是不同的instance。
s1 = 0x600001e58ed0
s2 = 0x600001e58f40
三、总结
单例模式作为一种目标明确、结构简单、理解容易的设计模式,在软件开发中使用频率相当高。
1、优点
1、单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
2、缺点
1、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
3、现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。
3、适用场景
1、系统只需要一个实例对象。
2、客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
4、iOS应用举例
比如UIApplication就是一个单例类,提供全局的应用服务,比如NSFileManager文件管理类。
Demo地址:iOS-Design-Patterns