前言:
单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
要点:
显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
一、单例的优缺点
1.单例的优点(主要优点)
实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例
因为其上面的特点,对于项目中的个别场景的传值,存储状态等等更加方便
2.单例的缺点(主要缺点)
单例实例一旦创建,对象指针是保存在静态区的,那么在堆区分配空间只有在应用程序终止后才会被释放
单例不能继承
二、单例的几种写法
OC
1.静态指针方式(单线程模式单例)
///.h
@interface SingleExample : NSObject
+ (instancetype)sharedInstance;
@property (nonatomic, copy) NSString *order_id;
@end
///.m
#import "SingleExample.h"
@implementation SingleExample
+ (instancetype)sharedInstance{
static SingleExample *myInstance = nil;
if(myInstance == nil){
myInstance = [[SingleExample alloc]init];
}
return myInstance;
}
@end
///测试
SingleExample *model = [SingleExample sharedInstance];
model.order_id = @"0001";
NSLog(@"------%@-------",model.order_id)
2.多线程加锁单例
/**
dispatch_once 无论使用多线程还是单线程,都只执行一次
GCD创建单例不仅可以解决多条线程的线程安全问题,也能保证性能,是官方荐的方式。
dispatch_once主要是根据onceToken的值来决定怎么去执行代码。
当onceToken = 0时,线程执行dispatch_once的block中代码
当onceToken =- 1时,线程跳过dispatch_once的block中代码不执行
当onceToken为其他值时,线程被阻塞,等待onceToken值改变
dispatch_once执行的流程
当线程调用shareInstance,此时onceToken = 0,调用block中的代码。此onceToken的值变为72。
当其他线程再调用shareInstance方法时,onceToken的值已经是72了,线程阻塞。
当block线程执行完block之后,onceToken变为-1.其他线程不再阻塞,跳过block。
下次再调用shareInstance时,block已经为-1.直接跳过block。
*/
///.h
@interface SingleExample : NSObject
+ (SingleExample *)shareInstance;
@property (nonatomic, copy) NSString *order_id;
@end
@implementation ShareManage
///.m
+ (SingleExample *) shareInstance {
static SingleExample * single = nil;
static dispatch_once_t onceToken ;
NSLog(@"onceToken开始::::::%ld",onceToken);
dispatch_once(&onceToken, ^{
shareInstance =[[SingleExample alloc]init];
NSLog(@"onceToken执行中::::::%ld",onceToken);
// [NSThread sleepForTimeInterval:10];
}) ;
NSLog(@"onceToken执行结束::::::%ld",onceToken);
return shareInstance;
}
@end
///测试
SingleExample *model = [SingleExample shareInstance];
model.order_id= @"0002";
NSLog(@"------%@-------",model.order_id)
3.单例的健壮性
///.h
@interface ShareManage : NSObject
+ (instancetype)shareManager;
@property (nonatomic, strong) NSString *NAME;
@end
///.m
#import "ShareManage.h"
@interface ShareManage()<NSCopying,NSMutableCopying>
@end
static ShareManage *manager = nil;
@implementation ShareManage
+ (instancetype)shareManager{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc]init];
//不是使用alloc方法,而是调用[[super allocWithZone:NULL] init]
//已经重载allocWithZone基本的对象分配方法,所以要借用父类(NSObject)的功能来帮助出处理底层内存分配的杂物
});
return manager;
}
///用alloc返回也是唯一实例
+ (id)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [super allocWithZone:zone];
});
return manager;
}
///对对象使用copy也是返回唯一实例
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
return manager;
}
///对对象使用mutablecopy也是返回唯一实例
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
return manager;
}
/*
当static关键字修饰局部变量时,只会初始化一次且在程序中只有一份内存
allocWithZone mutablecopyWithZone 这个类遵守<NSCopying,NSMutableCopying>协议
如果_instance = [self alloc] init];创建的话,
将会和-(id) allocWithZone:(struct _NSZone *)zone产生死锁。
dispatch_once中的onceToken线程被阻塞,等待onceToken值改变。
当用alloc创建对象、以及对对象进行copy mutableCopy也是返回唯一实例
*/
@end
///测试
ShareManage *manage = [ShareManage shareManager];
manage.NAME = @"测试单例,输出名称";
NSLog(@"------%@-------",manage.NAME)
Swift
1.最简单的 全局变量方法(“单行单例”方法) 通过 static 关键字创建
class SingletonOne {
static let sharedInstance = SingletonOne()
private init() {}
//切记私有化初始化方法,防止外部通过init直接创建实例。
}
2.严格单例模式 继承自 NSObject
class SingletonClass: NSObject {
static let shared = SingletonClass()
// Make sure the class has only one instance
// Should not init or copy outside
private override init() {}
override func copy() -> Any {
return self // SingletonClass.shared
}
override func mutableCopy() -> Any {
return self // SingletonClass.shared
}
// Optional
func reset() {
// Reset all properties to default value
}
}
/**
静态属性 shared 持有唯一的实例,对外公开。
重载 init() 方法,使其对外不可见,不可以在外部调用,防止在外部创建实例。
重载 copy()、mutableCopy() 方法,返回 self,防止在外部复制实例。这里也可以返回 SingletonClass.shared,效果是一样的,因为只有一个实例。只有 shared 能调用 copy()、mutableCopy() 方法,那么 self 就是 shared。写 self,代码比较简洁。
单例一旦创建,一直持有,不能手动销毁,但可以重置数据。如果需要的话,可以添加一个重置数据的方法 reset()。例如,当前用户退出登录,需要把当前用户实例的所有属性重置为默认值,防止数据错误。
*/
3.不继承自 NSObject
class SingletonClass2 {
static let shared = SingletonClass2()
// Make sure the class has only one instance
// Should not init outside
private init() {}
// Optional
func reset() {
// Reset all properties to default value
}
}
///不继承自 NSObject 的类没有 copy()、mutableCopy() 方法,不需要重载。
4.方法内定义静态常量
class SingleFour :NSObject{
var tValue = "df"
static var sharedInstance : SingleFour {
struct Static {
static let instance : SingleFour = SingleFour()
}
return Static.instance
}
}
或
class ShareManage: NSObject {
var tValue = "df"
class var shareOnce : ShareManage {
struct tOnce {
static let instance = ShareManage()
}
return tOnce.instance
}
}
///测试
ShareManage.sharedInstance.tValue = "1001"
print("123456789" + ShareManage.sharedInstance.tValue)
5.全局的常量
let single = SingleSix()
class SingleSix {
var tValue = "df"
class var sharedInstance : SingleSix {
return single
}
}
///测试
ShareManage.sharedInstance.tValue = "1001"
print("123456789" + ShareManage.sharedInstance.tValue)
6.使用GCD技术实现的单例模式 使用dispatch_once可以保证其中的代码只执行一次
/**
class SwiftSingleton {
class var shared: SwiftSingleton {
dispatch_once(&Inner.token) {
Inner.instance = SwiftSingleton()
}
return Inner.instance!
}
struct Inner {
static var instance: SwiftSingleton?
static var token: dispatch_once_t = 0
}
}
///swift3.0之后 dispatch_once_t、dispatch_once不可用。修改为下面代码
*/
var sharedInstance : ShareManage {
struct Inner {
static var instance: ShareManage?
static var token = {0}()
}
_ = Inner.token
return Inner.instance!
}
class ShareManage {
static let sharedInstance: ShareManage = { ShareManage() }()
var shareName = ""
}
///测试
ShareManage.sharedInstance.shareName = "100"
print("123456789" + ShareManage.sharedInstance.shareName)
三、系统中的单例
UIApplication(应用程序实例)
NSNotificationCenter(消息中心):
NSFileManager(文件管理):
NSUserDefaults(应用程序设置):
NSURLCache(请求缓存):
NSHTTPCookieStorage(应用程序cookies池):
END