什么是单例模式和单例类
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问。比如你上了一个需要注册的网站,然后你点一下注册按钮就会跳出来一个需要填写信息的页面,如果你没有对这个页面使用单例模式 你再点一下注册,就会又跳出一个相同的页面,使用单例模式过后,作用就是在没有把上一个填写信息的页面关闭之前是不会再跳出一个相同的页面的,。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
而单例类在编译时进行初始化,然后一直保存在内存中,直到程序退出时由系统自动释放这部分内存。只要程序还在运行就会一直占用内存。
其实系统给我们提供了很多的单例类。
[UIApplication sharedApplication]; //应用程序实例类
[NSNotificationCenter defaultCenter];//消息中心实例类
[NSFileManager defaultManager];//文件管理实例类
[NSUserDefaults standardUserDefaults];//应用程序设置
[NSURLCache sharedURLCache];//请求缓存实例类
单例模式的优缺点
优点
1、整个项目中只会实例化一次,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
2、在整个项目中只实例化一个对象,节省系统内存,提高程序的运行效率
3、避免对共享资源的多重占用。
缺点
1、不能被重写、不能被继承、不能被扩展
2、创建单例对象之后,只要程序一直运行就一直占用这系统的内存,在不用该对象的时候也不能销毁消耗着系统的内存
3、不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
在简单了解了单例模式之后,我们就可以开始创建一个单例模式了:
为什么不使用全局变量确保一个类只有一个实例呢?
我们知道全局变量分为静态变量和实例变量,静态变量也可以保证该类的实例只存在一个。
只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
但是,如果说这个对象非常消耗资源,而且程序某次的执行中一直没用,这样就造成了资源的浪费。利用单例模式的话,我们就可以实现在需要使用时才创建对象,这样就避免了不必要的资源浪费。 不仅仅是因为这个原因,在程序中我们要尽量避免全局变量的使用,大量使用全局变量给程序的调试、维护等带来困难。
单例模式的创建
1.饿汉式:单例实例在类装载时就构建,急切初始化。(预先加载法)
* 饿汉式(推荐)
*
*/
public class Test {
private Test() {
}
public static Test instance = new Test();
public Test getInstance() {
return instance;
}
}
优点
1.线程安全
2.在类加载的同时已经创建好一个静态对象,调用时反应速度快
缺点
资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
2、懒汉式:单例实例在第一次被使用时构建,延迟初始化。
创建一个类,继承于NSObject
// MYSingleton.h
@interface MYSingleton : NSObject
//方法前面一个加号表示这个方法是一个类方法,类方法是无需实例化就可以直接用这个类去调用的方法 不需要分配内存空间。
//获取单例
+ (instancetype)shareSingleton;
@end
// MYSingleton.m
//定义一个全局变量
//为单例对象实现一个静态实例,并初始化,然后设置成nil,
static MYSingleton *_instance = nil;
//实现一个实例构造方法检查上面声明的静态实例是否为nil,
//如果是则新建并返回一个本类的实例,
+ (MYSingleton)shareSingleton {
@synchronized(self) {
if(_instance == nil) {
[[self alloc] init];
}
}
return _instance;
}
//不管是使用alloc还是使用allocWithZone方法,其默认执行的都是allocWithZone方法。
//所以这里选择重写了allocWithZone方法,
//所以可以保证其他人直接使用alloc和init试图获得一个新实例时不产生一个新实例,
+ (id)allocWithZone:(NSZone *)zone {
@synchronized(self) {
if(_instance == nil) {
[[self alloc] init];
return _instance;
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return self;
}
//viewController.m
- (void)viewDidLoad {
[super viewDidLoad];
MYSingleton *singleton = [ MYSingleton shareSingleton];
MYSingleton *allocThis = [[ MYSingleton alloc] init];
NSLog(@"%@, %@", singleton, allocThis);
}
上面控制通过shareSingleton()和alloc方法来获取MYSingleton实例时,程序最多只会产生一个Singleton实例。在viewDidLoad()函数中调用两种方法,并且打印它们各自的地址,可以发现地址是一样的,所以程序中自始至终都只有一个单例实例。
优点:
避免了饿汉式的那种在没有用到的情况下创建事例,资源利用率高,不执行getInstance()就不会被实例,可以执行该类的其他静态方法。
缺点:
懒汉式在单个线程中没有问题,但多个线程同事访问的时候就可能同事创建多个实例,而且这多个实例不是同一个对象,虽然后面创建的实例会覆盖先创建的实例,但是还是会存在拿到不同对象的情况。解决这个问题的办法就是加锁synchonized,第一次加载时不够快,多线程使用不必要的同步开销大。
下面这个就是苹果官方的单例写法:
要是对写法还有疑惑的,可以参考苹果官方的单例写法。