本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢。
单例模式,由于其使得类的一个对象成为系统中的唯一实例,同时提供了对唯一实例的受控访问,是在开发中常用的设计模式,本文主要记录我自己在开发中如何使用单例并手动销毁的。
单例模式是什么
- 单例模式定义:简单的来说,一个单例类,在整个程序中只有一个实例,并且提供一个类方法供全局调用,在编译时初始化这个类,然后一直保存在内存中,程序(APP)退出时由系统自动释放这部分内存。
系统常见的单例
UIApplication (应用程序实例类)
NSNotificationCenter (消息中心类)
NSFileManager (文件管理类)
NSUserDefaults (应用程序设置)
NSURLCache (请求缓存类)
NSHTTPCookieStorage (应用程序cookies池)
单例的应用场景
(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。
(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。
单例的生命周期
请看下面的表格展示了程序中中不同的变量在手机存储器中的存储位置
在程序中,一个单例类在程序中只能初始化一次,为了保证在使用中始终都是存在的,所以单例是在存储器的全局区域,在编译时分配内存,只要程序还在运行就会一直占用内存,在APP结束后由系统释放这部分内存内存。
新建一个单例类
- 单例模式的创建方式
同步锁 :NSLock
@synchronized(self) {}
信号量控制并发:dispatch_semaphore_t
条件锁:NSConditionLock
GCD : dispatch_once_t
其中采用GCD 创建单例是线程安全,苹果官方推荐开发者使用dispatch_once_t来创建单例,那么我就采用dispatch_once_t方法来创建一个单例, 类名为ASharedManage。
由于一个继承自NSObject的对象可以通过多种方式创建如:alloc 、new 、copy、mutableCopy . 如果同事用在项目中用到了这个类,又不知道这是一个单例,他通过alloc init 来创建出来这个对象就跟通过sharedManage方法创建出来的不是同一对象了,这不是我们想看到的结果,所以需要重写这些方法来保证不论通过哪种方式创建出来的必须为同一对象。如:
//单利方法
+ (instancetype)shareInstance {
//注意下面这行代码直接写在这里就可以,不用担心多次调用出问题;1.static id instance = nil;只是编一阶段的初始值,真正赋值后就是一个唯一的值 2. onceToken地址没有被释放就不会再次进入dispatch_once函数
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[super allocWithZone:NULL] init];
});
return instance;
}
//alloc new 方式创建实例最终都会执行这个方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [[self class] shareInstance];
}
//copy mutableCopy 方式创建实例最终都会执行这个方法
- (instancetype)copyWithZone:(struct _NSZone *)zone{
return [[self class] shareInstance];
}
补充:static可以多次赋值,但最好不要那么做,负责会失去静态变量的意义
测试单利是否有效 ???
ASharedManage *manage1 = [[ASharedManage alloc]init];
ASharedManage *manage2 = [[ASharedManage alloc]init];
ASharedManage *manage3 = [[ASharedManage alloc]init];
ASharedManage *manage4 = [ASharedManage sharedLoginQuery];
ASharedManage *manage5 = [ASharedManage sharedLoginQuery];
ASharedManage *manage6 = [ASharedManage sharedLoginQuery];
ASharedManage *manage7 = [manage6 copy];
ASharedManage *manage8 = [manage5 copy];
manage1 0x6000029884f0
manage2 0x6000029884f0
manage3 0x6000029884f0
manage4 0x6000029884f0
manage5 0x6000029884f0
manage6 0x6000029884f0
manage7 0x6000029884f0
manage8 0x6000029884f0
如此就可以保证创建出来的是同一的实例了 !
单例的优缺点
主要优点:
1、提供了对唯一实例的接受访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3、允许可变数目的实例。
主要缺点:
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
单例宏定义
由于单例对象在开发中可能用到的不止一次,实现方式都一样,所以抽取成为宏来使用就成为必不可少的操作。代码如下:
#define ASingleH(className)\
+(instancetype)share##className;\
+(void)destruction##className;\
#if __has_feature(objc_arc)
#define ASingleM(className) static id _instance;\
static dispatch_once_t aonceToken;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
\
+(instancetype)share##className\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [[className alloc]init];\
});\
return _instance;\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
+(instancetype)alloc{\
if(_instance)\
{ return _instance;\
}\
return [super alloc];\
}\
-(instancetype)init{\
if(_instance)\
{ return _instance;}\
return [super init];\
}\
-(id)copy{\
if(_instance)\
{\
return _instance;\
}\
return [super copy];\
}\
- (id)mutableCopy\
{\
if(_instance)\
{\
return _instance;\
}\
return [super copy];\
}\
+(void)destruction##className{\
_instance = nil;\
aonceToken =0l;\
}
#endif