iOS设计模式之单例模式
1、什么是单例模式
- 单例模式的定义
某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。Ensures a class has only one instance, and provide a global point of access to it.保证一个类只有一个实例,并且提供一个全局的访问入口访问这个实例。 - 单例类举例
-
UIApplication(应用程序实例类)
-
NSNotificationCenter(消息中心类)
-
NSFileManager(文件管理类)
-
NSUserDefaults(应用程序设置)
-
NSURLCache(请求缓存类)
-
NSHTTPCookieStorage(应用程序cookies池)
-
- 单例类使用时机?
-
官方说法:
一个类必须只有一个对象。客户端必须通过一个众所周知的入口访问这个对象。
这个唯一的对象需要扩展的时候,只能通过子类化的方式。客户端的代码能够不需要任何修改就能够使用扩展后的对象。 -
举例说明:在建模的时候,如果这个东西确实只需要一个对象,多余的对象都是无意义的,那么就考虑用单例模式。比如,存储用户信息的用户管理类,在登录成功后,其它各个业务模块都可能用到。所以就会考虑用单例。
-
- 单例模式有什么优缺点?
- 内存问题
单例其实延长了对象的声明周期,在整个程序的声明周期中都是存在的,系统不能及时回收,就会一直占用内存。如果单例类所占用的内存大的化,问题可能更严重。又或者单例本身不是很大,但是他强引用了另外的比较大的对象的时候,那么那个比较大的对象就由于单例没有被释放,他也不会被释放,这样也造成了内存压力。- 解决方法:
针对这种单例本身不大,强引用对象比较大的情况,解决方式:比如一些可以重新加载的对象,需要的时候强引用,使用完成之后,不在强引用,释放掉内存,下次使用的时候再加载回来。
- 解决方法:
- 循环依赖
因为单例在创建之后,生命周期直到程序直到程序结束后才结束,当两个单例类内的属性创建都彼此依赖于对方时,就会造成死锁。借用网上的例子
在单例A中创建m属性,这个属性在单例A的init方法中进行初始化,初始化的时候m属性依赖于另一个单例B创建。而单例B中创建了n属性,这个属性在单例B的init中进行初始化,初始化的时候n属性依赖于前面的单例A创建。这样创建出来会使用其中一个单例进行初始化的时候会出现死锁问题。
-
解决方法
-
设计的时候就尽量不去产生这种互相依赖的关系。
-
如果真的产生这种依赖关系就适当的打破死锁链。
-
实在要形成环或者无法控制,那么就异步初始化的方式,先过去,内容再填,内部需要做个标识,标识这个单例创建的是时候不能立即被使用或者不能完整被使用。
-
-
- 内存问题
2、单例类的生命周期
- 不同的变量在手机存储器中的存储位置
位 置 | 存放的变量 |
---|---|
栈 | 临时变量(由编译器管理自动创建/分配/释放的,栈中的内存被调用时处于存储空间中,调用完毕后由系统系统自动释放内存) |
堆 | 通过alloc、calloc、malloc或new申请内存,由开发者手动在调用之后通过free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存,在ARC模式下,由系统自动管理。 |
全局区域 | 静态变量(编译时分配,APP结束时由系统释放) |
常量 | 常量(编译时分配,APP结束时由系统释放) |
代码区 | 存放代码 |
- 单例在手机存储器中的位置
单例对象一旦建立,对象指针保存在静态区,单例对象在堆中分配的内存空间,只在应用程序终止后才会被释放
1. 单例模式创建的对象能够一直存在于内存中不被释放,并不只是由于持有一个自身的引用,本质是因为这个引用是静态,也就是说,如果成员变量是非静态的,它持有一个自身的引用,那么这个对象还是被回收。
2. "系统内至少保持一个对象的引用",这个引用指的是从栈区或方法区中发出的引用,也就是安全的引用
3. 类被实例化之后,是放在堆区中的,而我们是无法直接操作堆内存的,因此需要一个引用,指向堆区的某个区域,而这个引用,必须是从栈中(或方法区中)发出的,因为我们可以直接访问栈内存,如果是从堆中发出的引用,是无意义的引用,我们根本访问不到,因此会被回收。
4. 在程序中,一个单例类在程序中只能初始化一次,为了保证在使用中始终都是存在的,所以单例是在存储器的全局区域,在编译时分配内存,只要程序还在运行就会一直占用内存,在APP结束后由系统释放这部分内存内存。 - 流程图
3、新建一个单例类
- 一般单例模式的创建方式(原则保存只创建一次);
- .h
@interface SingletonClass : NSObject + (instancetype)shareInstance; @end
-
.m
+ (instancetype)shareInstance { return [[super alloc]init]; } /** * @see 使用alloc方法初始化一个类的实例的时候,默认是调用了allocWithZone的方法 */ + (instancetype)allocWithZone:(struct _NSZone *)zone { static SingletonClass *instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super allocWithZone:zone]; }); return instance; }
- .h
二,demo
单例