单例(singleton)
-
意图
- 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
-
适用性
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它
- 当这个唯一的实例应该是通过子类化可以扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时
-
结构
-
优缺点
- 对唯一实例的受控访问
- 缩小命名空间
- 允许对操作和表示的精化
- 允许可变数目的实例
- 比类操作更加灵活
-
实现
- OC版本
// 非线程安全 @interface Singleton: NSObject +(instancetype)sharedInstance; @end @implementation Singleton static Singleton* instance = nil; // 程序运行时就初始化该单例 //+(void)load { // [Singleton sharedInstance]; //} +(instancetype)sharedInstance { if(instance == nil){ instance = [[Singleton alloc] init]; } return instance; } //当我们调用alloc时候回调改方法(保证唯一性),oc不允许构造方法私有化 +(id)allocWithZone:(struct _NSZone *)zone { if(instance == nil){ instance = [super allocWithZone:zone]; } return instance; } @end // 线程安全 @implementation Singleton static Singleton* instance = nil; +(instancetype)sharedInstance { if(instance == nil) { // 加锁解锁是需要消耗性能的 @synchronized (self) { if(instance == nil) { instance = [[Singleton alloc] init]; } } } return instance; } +(id)allocWithZone:(struct _NSZone *)zone { @synchronized (self) { if(instance == nil) { instance = [super allocWithZone:zone]; } } return instance; } @end // 线程安全-GCD @implementation Singleton static Singleton* instance = nil; +(instancetype)sharedInstance { static dispatch_once_t once; dispatch_once(&once, ^{ instance = [[Singleton03 alloc] init]; }); return instance; } // 2017.8.10 更新,这里的做法是不对的,原因请看下文 //当我们调用alloc时候回调改方法(保证唯一性),oc不允许构造方法私有化 +(id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t once; dispatch_once(&once, ^{ instance = [super allocWithZone:zone]; }); return instance; } @end
华丽的分割线,感觉慧慧大神指出 OC 版单例的一处错误,上面所以说的用 allocWithZone 来保持唯一性的做法不是太合理,应该直接禁用 alloc 和 init 方法:
+ (id)alloc NS_UNAVAILABLE; - (instancetype) init NS_UNAVAILABLE;
- Swift版本
// 非线程安全 final class Singleton: NSObject { private static var instance: Singleton? = nil class func sharedInstance() -> Singleton { if instance == nil { instance = Singleton() } return instance! } // 将构造方法私有化 private override init() { } } // 线程安全 final class Singleton: NSObject { static var sharedInstance = Singleton() private override init() { } }
对于如何选择线程安全或者非线程安全的单例呢,如果程序不涉及多线程时,就优先选择非线程安全吧,毕竟加锁解锁还是需要消耗资源的。