1.单例介绍
单例:该类在程序运行期间有且仅有一个实例。
为了我们能更好的理解单例模式,我列举以下几个cocoa框架中常用的单例:
1.UIApplication:应用程序。一个UIApplication对象就代表着一个应用程序,每个应用程序有且仅有一个UIApplication对象,开发中最常用的是使用它的openURL函数来跳转到其他应用程序,通过
[UIApplication sharedApplication] 类方法可以获得。2.NSNotificationCenter:通知中心。iOS中的通知中心是一种消息广播,采用观察者模式和单例模式,一个应用有且仅有一个通知中心。通过
[NSNotificationCenter defaultCenter] 类方法可以获得。3.NSFileManager:文件管理器。它是iOS文件系统的接口,用来创建、修改、访问文件。一个应用有且仅有一个文件管理器。通过 [NSFileManager defaultManager] 类方法可以获得。
4.NSUserDefaults:用户偏好设置。它主要用来存储简单的键值对数据,数据持久化最简单和基础的一种方案。通过 [NSUserDefaults standardUserDefaults] 类方法可以获得。
5.NSURLCache:URL缓存。通过将NSURLRequest对象映射到NSCachedURLResponse对象来实现对URL加载请求的响应的缓存。
通过 [NSURLCache sharedURLCache] 类方法可以获得.
1.1单例模式的要点
1.该类有且只有一个实例;
2.该类必须能够自行创建这个实例;
3.该类必须能够自行向整个系统提供这个实例;
1.2单例的主要优点
1.单例可以保证系统中该类有且仅有一个实例。确保所有对象都访问这个唯一实例;
2.因为类控制了实例化对象,所以类可以灵活更改实例化过程;
3.基于第一条,对于项目中的个别场景的传值,存储状态等业务更加方便;
4.可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能;
1.3单例的主要缺点
1.由于单例模式没有抽象层,因此单例类的扩展有很大的困难。单例不能被继承,不能有子类。
2.不易被重写或扩展 (可以使用分类)
3.单例实例一旦创建,对象指针是保存在静态区,那么在堆区分配的空间只能在应用程序终止才会被释放;
4.单例类的指责过重,在一定程度上违背了“单一职责的原则”;
2.单例的生命周期
首先复习一下内存的五大存储区域
在程序中,一个单例类在程序中只能初始化一次,为了保证在使用中始终都是存在的,所以单例是在存储器的全局区域,在编译时分配内存,只要程序还在运行就会一直占用内存,在app结束后有系统释放这部分内存。
单例的静态变量被指为nil是否内存会得到释放?
将单例类实例对象赋值为nil后,会触发单例的dealloc方法,静态变量修饰的指针保存在了全局区域,不会释放。但指针保存的首地址关联的对象是保存在堆区的,是会释放的。
单例的实现
关键字static
1.static修饰局部变量
普通的局部变量创建后是放在栈区中,这种局部变量进入作用域时创建,出了作用域就销毁,并且只初始化一次。static修饰局部变量这放在静态区中,它改变了局部变量的存储位置,从而使它的生命周期延长,直至程序结束才销毁
static修饰局部变量只改变生命周期,不改变作用域。
2.static修饰全局变量
全局变量存储在静态区,它的作用域十分的广,只要在一个源文件中定义后,这个程序中的所有源文件、对象以及函数都可以调用,生命周期更是贯穿整个程序。文件中的全局变量想要被另一个文件使用时就需要进行外部声明(以下用extern关键字进行声明)。其本质是:全局变量本身是具有外部链接属性的,在A文件中定义的全局变量,在B文件中可以通过【链接】来使用;
当static修饰全局变量,那这个外部链接属性就会被修改成内部链接属性,此时这个全局变量就只能在自己的源文件中使用,就无法在另一个文件用extern关键字声明使用。
static修饰全局变量不改变生命周期,单缩小了作用域。
这里用static修饰单例,防止在其他类中通过extern关键字声明对单例进行修改。
单例的实现重点就是防止在外部调用的时候出现多个不同的实例,也就是在创建的方式入手禁止出现多个不同的实例
主要做到以下几点:
1.防止调用 [[A alloc] init] 引起错误
2.防止调用 new 引起错误
3.防止调用 copy 引起错误
4.防止调用 mutableCopy 引起错误
典型的单例写法
static id instance = nil;
+(id) crestInstance {
//如果全区变量为nil
if (!instance) {
//创建一个实例,并将该实例赋给instance全区变量
instance = [[self alloc] init];
}
retrurn instance
}
缺点:无法保证多线程情况下只创建一个对象。适用于单线程。
加锁的方法
static Singleton *_sharedSingleton = nil;
+(instancetype)sharedSingleton {
@synchronized(self){ //加锁,保证多线程下也只能有一个线程进入
if (! _sharedSingleton) {
_sharedSingleton = [[self alloc] init];
}
}
return _sharedSingleton;
}
免锁的方法
static Singleton *_sharedSingleton = nil;
+ (instancetype)sharedSingleton {
static BOOL initialized = NO;//确保只有在第一次调用sharedSingletion方法才会创建实例。在后续调用时,直接返回之前创建的实例,避免重复创建。
if (initialized == NO){
initialized = YES;
_sharedSingleton = [[self alloc] init];
}
return _sharedSingleton;
}
3.单例模式的类型
懒汉模式:在真正需要使用对象时才去创建该单例类对象
饿汉模式:在类加载时已经创建好该单例对象,等待程序使用;
懒汉模式创建单例对象是在程序使用对象前,先判断对象是否实例化(判空),若已实例化直接返回该类对象,否者先执行实例化操作。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Singletion : NSObject<NSMutableCopying, NSCopying>
+ (instancetype)sharedSingletion;
@end
NS_ASSUME_NONNULL_END
#import "Singletion.h"
@implementation Singletion
static id _instance;
//alloc方法内部会调用这个方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (_instance == nil) { //防止频繁加锁
@synchronized (self) {
if (_instance == nil) { //防止创建多次
_instance = [super allocWithZone: zone];
}
}
}
return _instance;
}
//直接调用类方法
+ (instancetype)sharedSingletion {
if (_instance == nil) {
@synchronized (self) {
if (_instance == nil) {
_instance = [[self alloc] init];
}
}
}
return _instance;
}
//copy方法内部会调用这个方法
- (id)copyWithZone:(NSZone *)zone {
return _instance;
}
//mutablecopy方法会调用这个方法
- (id)mutableCopyWithZone:(NSZone *)zone {
return _instance;
}
@end
@synchronized的作用是创建一个互斥锁,保证此时没有其他线程对self对象进行修改,保证代码的安全性。也就是包装这段代码的原子性的,安全的。这个是OC的一个锁定令牌,防止self对象在同一时间内被其他线程访问,起到保护线程安全的作用。
copyWithZone:和mutableCopyWithZone:是对自定义对象copy和mutableCopy时会用到,这里重写以确保复制操作也得到同一单例对象的引用,而不会创建新的对象副本。
饿汉式创建单例对象,在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去调用
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface singletion_ehan : NSObject<NSMutableCopying, NSCopying>
+ (instancetype)sharedSingletion;
@end
NS_ASSUME_NONNULL_END
#import "singletion ehan.h"
@implementation singletion_ehan
static id _instance;
//当类加载到OC运行时环境中(内存),就会调用一次(一个类只会加载一次)
+ (void)load {
_instance = [[self alloc] init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (_instance == nil) {//防止多次创建
_instance = [super allocWithZone:zone];
}
return _instance;
}
+ (id)sharedSingletion {
return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return _instance;
}
@end```