前言
一提到 iOS 单例模式,一般都会想到如下常用方式。
+ (instancetype)sharedInstance {
static TestClass *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
大家按约定使用 sharedInstance
获取单例,似乎没有什么问题,大家也都是这么做的。
其实,单例模式其实不只是仅仅一个 sharedInstance
方法就够了
完整写法
避免使用方使用 alloc、new 和 copy、mutableCopy,有两种处理办法
写法一
+ (instancetype)sharedInstance {
static TestClass *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
return [self sharedInstance];
}
- (id)copyWithZone:(struct _NSZone *)zone
{
return [TestClass sharedInstance];
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
return [TestClass sharedInstance];
}
写法二
+ (instancetype)sharedInstance {
static TestClass *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
# .h 中禁用方法调用
+ (instancetype)new __attribute__((unavailable("Use +sharedInstance instead")));
- (instancetype)init __attribute__((unavailable("Use +sharedInstance instead")));
- (instancetype) copy __attribute__((unavailable("Use +sharedInstance instead")));
- (instancetype) mutableCopy __attribute__((unavailable("Use +sharedInstance instead")));
# 或者
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)copy NS_UNAVAILABLE;
- (instancetype)mutableCopy NS_UNAVAILABLE;
重谈单例
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
为保证进程内有且仅有一个实例可以供外部访问,需要满足下面条件:
- 私有化构造函数
- 私有的静态变量存储唯一实例
- 公开静态方法获取唯一实例
实现单例需要考虑的问题有:
- 创建单例是否延迟加载
- 创建单例是否线程安全
- 获取单例是否加锁,加锁性能如何保证
饿汉式
类加载的时候,静态实例 instance 就已创建并初始化
public class Singleton {
// 静态字段引用唯一实例:
private static final Singleton INSTANCE = new Singleton();
// 通过静态方法返回实例:
public static Singleton getInstance() {
return INSTANCE;
}
// private构造方法保证外部无法实例化:
private Singleton() {
}
}
懒汉式
调用方第一次调用getInstance()时才初始化全局唯一实例
public class Singleton {
private static final Singleton INSTANCE = null;
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
private Singleton() {
}
}
双重检查
由于懒汉式加锁会严重影响并发性能
public class Singleton {
private static final Singleton INSTANCE = null;
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
private Singleton() {
}
}
需要注意: 在低版本 Java 中,注意指令重排,需要给 INSTANCE 加 volatile,禁止指令重排,高版本无需关心。
由于 Java 的内存模型,双重检查在这里不成立。要真正实现延迟加载,只能通过 Java 的 ClassLoader 机制完成。
静态内部类
Java 加载外部类的时候,不会创建内部类的实例,只有在外部类使用到内部类的时候才会创建内部类实例。
public class Singleton {
public static Singleton getInstance() {
return SingletonInner.instance;
}
private static class SingletonInner {
private static final Singleton instance = new Singleton();
}
private Singleton() {
}
}
枚举
Java 保证枚举类的每个枚举都是单例,所以编写一个只有一个枚举的类,可以实现单例
public enum Test {
INSTANCE;
private String name = "test";
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}