基础——单例创建

一、单线程模式单例

+(instancetype)sharedSingleton
{
  static Singleton *singleton;
  if (!singleton) {
    singleton = [[Singleton alloc] init];
  }
  return singleton;
}
  • 单线程单例只有在单个线程使用的情况下实用,在多线程的情况下,会产生线程不安全的情况。严格意义上来说,我们还需要把alloc方法变为私有方法才行,严格的单例是不允许再创建其他实例的,而alloc方法可以在外部任意生成实例。换句话说,假如在两条线程里调用sharedLoadData方法,可能会产生两个singleton实例,这样单例就失去意义了。

二、多线程加锁单例

// @synchronized加锁
+(instancetype)sharedSingleton
{
  static Singleton *singleton;
  @synchronized (self) {
  if (!singleton) {
    singleton = [[Singleton alloc] init];
   }
  }
  return singleton;
}
  • 加锁以后,当多个线程同时调用shareInstance时,由于@synchronized已经加锁,只能有一个线程创建singleton实例。这样就解决了第一种情况的弊端。
    但是也有缺点:只有在singleton未创建时,加锁才是必要的。如果singleton已经创建,这个时候还加锁的话,会影响性能。

三、系统GCD创建单例

+(instancetype)sharedLoadData
{
  static Singleton *singleton = nil;
  static dispatch_once_t onceToken;
  // dispatch_once 无论使用多线程还是单线程,都只执行一次
  dispatch_once(&onceToken, ^{
    singleton = [[Singleton alloc] init];
  });
  return singleton;
}
  • GCD创建单例不仅可以解决多条线程的线程安全问题,也能保证性能,是官方推荐的方式。

  • dispatch_once主要是根据onceToken的值来决定怎么去执行代码。
    1.当onceToken = 0时,线程执行dispatch_once的block中代码
    2.当onceToken = -1时,线程跳过dispatch_once的block中代码不执行
    3.当onceToken为其他值时,线程被阻塞,等待onceToken值改变

  • 当线程调用shareInstance,此时onceToken = 0,调用block中的代码,此时onceToken的值变为140734537148864。当其他线程再调用shareInstance方法时,onceToken的值已经是140734537148864了,线程阻塞。当block线程执行完block之后,onceToken变为-1.其他线程不再阻塞,跳过block。下次再调用shareInstance时,block已经为-1.直接跳过block。

优化完善

  • 第二步
    防止alloc] init] 和new引起的错误,因为alloc] init] 和 new 实际是一样的工作原理.都是执行了下面方法
// 防止外部调用alloc 或者 new
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
  return [Singleton sharedSingleton];
}
  • 第三步 NSCopying
    防止copy引起的错误,个人感觉当你的单例类不遵循NSCopying协议,外部调用本身就会出错. 如果不是业务需求不遵循协议,该方法也可以不写 这都是个人观点 不过为了严谨还是写吧
// 防止外部调用copy
- (id)copyWithZone:(nullable NSZone *)zone {
  return [Singleton sharedSingleton];
}
  • 第四步
    防止mutableCopy引起的错误,个人感觉当你的单例类不遵循NSMutableCopying协议,外部调用本身就会出错. 如果不是业务需求不遵循协议,该方法也可以不写 这都是个人观点 不过为了严谨还是写吧
// 防止外部调用mutableCopy
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
  return [Singleton sharedSingleton];
}

 

实现方法二

上面的方法是把其可能出现的初始化方法做了相应的处理来其保证安全性
其实我们可以在不做处理的情况下 禁止外部调用岂不是更简单 个人感觉该方法也很不错而且一些成熟的第三方中的单例中也有使用该方法的

直接在你创建的单例文件的.h文件中加入代码

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (id)copy NS_UNAVAILABLE; // 没有遵循协议可以不写
- (id)mutableCopy NS_UNAVAILABLE; // 没有遵循协议可以不写

.m文件实现一个方法即可,上面方式的其他方法就不要写了

// 跟上面的方法实现有一点不同
+ (instancetype)sharedSingleton {
  static Singleton *_sharedSingleton = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
     // 要使用self来调用
    _sharedSingleton = [[self alloc] init];
  });
  return _sharedSingleton;
}