你好,我是Emma,今天我们针对单例进行简单的介绍和剖析。
前言:单例的介绍,比较有名是GOF的23中设计模式:
单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。Ensures a class has only one instance, and provide a global point of access to it.保证一个类只有一个实例,并且提供一个全局的访问入口访问这个实例。
| 范围\目的 | 创建型模式 | 结构型模式 | 行为型模式 |
|---|---|---|---|
| 类模式 | 工厂方法 | (类)适配器 | 模板方法、解释器 |
| 对象模式 | 单例 原型 抽象工厂 建造者 | 代理 (对象)适配器 桥接 装饰 外观 享元 组合 | 策略 命令 职责链 状态 观察者 中介者 迭代器 访问者 备忘录 |
官方说法:
一个类必须只有一个对象。客户端必须通过一个众所周知的入口访问这个对象。
这个唯一的对象需要扩展的时候,只能通过子类化的方式。客户端的代码能够不需要任何修改就能够使用扩展后的对象。
举个栗子:在建模的时候,如果这个东西确实只需要一个对象,多余的对象都是无意义的,那么就考虑用单例模式。比如定位管理(CLLocationManager),硬件设备就只有一个,弄再多的逻辑对象意义不大。所以就会考虑用单例。
举个栗子:在单例A中创建m属性,这个属性在单例A的init方法中进行初始化,初始化的时候m属性依赖于另一个单例B创建。而单例B中创建了n属性,这个属性在单例B的init中进行初始化,初始化的时候n属性依赖于前面的单例A创建。这样创建出来会使用其中一个单例进行初始化的时候会出现死锁问题。

面试实例:
1.单例实例化的对象存放在内存区域的哪个区?
单例对象一旦建立,对象指针保存在静态区,单例对象在堆中分配的内存空间,只在应用程序终止后才会被释放
单例模式创建的对象能够一直存在于内存中不被释放,并不只是由于持有一个自身的引用,本质是因为这个引用是静态,也就是说,如果成员变量是非静态的,它持有一个自身的引用,那么这个对象还是被回收。
"系统内至少保持一个对象的引用",这个引用指的是从栈区或方法区中发出的引用,也就是安全的引用
类被实例化之后,是放在堆区中的,而我们是无法直接操作堆内存的,因此需要一个引用,指向堆区的某个区域,而这个引用,必须是从栈中(或方法区中)发出的,因为我们可以直接访问栈内存,如果是从堆中发出的引用,是无意义的引用,我们根本访问不到,因此会被回收。
类的成员变量恰恰是放在堆内存中,因此由类的成员变量持有一个对象引用,这个引用是不安全的
2.内存区域分为那几个区?内存区域的划分和分配:
1.常用的单例应该如何去写?
单例比较方便的也是常用的方法是写成宏。
#define MYY_SINGLETON_DEF(_type_) + (_type_ *)sharedInstance; +(instancetype) alloc __attribute__((unavailable("call sharedInstance instead"))); +(instancetype) new __attribute__((unavailable("call sharedInstance instead"))); -(instancetype) copy __attribute__((unavailable("call sharedInstance instead"))); -(instancetype) mutableCopy __attribute__((unavailable("call sharedInstance instead")));
#define MYY_SINGLETON_IMP(_type_) + (_type_ *)sharedInstance{ static _type_ * theSharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ theSharedInstance = [[super alloc] init]; }); return theSharedInstance; }
使用方法:
//引用
@interface DJSingleton : NSObject
MYY_SINGLETON_DEF(DJSingleton);
@end
//实现
@implementation DJSingleton
MYY_SINGLETON_IMP(DJSingleton);
@end
另一种比较直接写代码的书写方法是:原理:dispatch_once_t来保证线程安全,从对象的创建角度出发:把创建对象的各种入口封死,比如:alloc,new,copy,无论通过哪种方式创建保证对象只创建一个。
具体实现:在对象创建的时候,无论是alloc还是new,都会调用到 allocWithZone方法。在通过拷贝的时候创建对象时,会调用到-(id)copyWithZone:(NSZone *)zone,
-(id)mutableCopyWithZone:(NSZone *)zone方法。因此,可以重写这些方法,让创建的对象唯一。
+(id)allocWithZone:(NSZone *)zone{
return [DJSingleton sharedInstance];
}
+(DJSingleton *) sharedInstance{
static DJSingleton * s_instance_dj_singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
s_instance_dj_singleton = [[super allocWithZone:nil] init];
});
return s_instance_dj_singleton;
}
-(id)copyWithZone:(NSZone *)zone{
return [DJSingleton sharedInstance];
}
-(id)mutableCopyWithZone:(NSZone *)zone{
return [DJSingleton sharedInstance];
}
参考文献:
原文:https://www.cnblogs.com/ma1314/p/11474917.html