单例加锁和不加锁的区别

一,单线程模式单例

// 单线程单例
+(instancetype)sharedLoadData
{
  static Singleton *singleton;
  if (!singleton ) {
    singleton = [[Singleton alloc] init];
  }
  return singleton;
}

1.单线程单例只有在单个线程使用的情况下使用,在多线程的情况下,会产生线程不安全的情况,严格意义上来说,我们还需要把alloc方法变为私有方法才行,严格的单例是不允许再创建其他实例的,而alloc方法可以在外部任意生产实例。换句话说,假如在两条线程里调用sharedLoadData方法,可能会产生两个singleton实例,这样单例就失去意义了。

二,多线程加锁单例

// @synchronized加锁
+(instancetype)sharedLoadData
{
    static Singleton *singleton;
    @synchronized (self) {
    if (!singleton) {
        singleton = [[Singleton alloc] init];
      }
    }
    return singleton;
}

2.加锁以后,当多个线程同时调用sharelnstanc时,由于@synchronized已经加锁,只能有一个线程创建singleton实例。这样就解决了第一种情况的弊端,但是也有缺点:只有在singletion未创建时,加锁才是必要的,如果singleton已经创建,这个时候还加锁的话,会影响性能。

三,系统GCD创建单例

+(instancetype)sharedLoadData
{
    static Singleton *singleton = nil;
    static dispatch_once_t onceToken;
    // dispatch_once  无论使用多线程还是单线程,都只执行一次
    dispatch_once(&onceToken, ^{
        singleton = [[Singleton alloc] init];
    });
    return singleton;
}

3.GCD创建单例不仅可以解决多条线程的线程安全问题,也能保证性能。

dispatch_one主要是根据onceToken的值来决定怎么去执行代码,

1.当onceToken = 0时,线程执行dispath_once的block中代码

2.当onceToken = -1时,线程跳过dispatch_one的block中的代码不执行,

3.当onceToken为其他值时,线程被阻塞,等待onceToken值改变。

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

四,单例实现方式与优缺点

1、懒汉模式:实现原理和懒加载其实很像,如果在程序中不使用这个对象,那么就不会创建,只有在你使用代码创建这个对象,才会创建。这种实现思想或者说是原理都是iOS开发中非常重要的,所以,懒汉式的单例模式也是最为重要的,是开发中最常见的。


2、饿汉模式:在没有使用代码去创建对象之前,这个对象已经加载好了,并且分配了内存空间,当你去使用代码创建的时候,实际上只是将这个原本创建好的对象拿出来而已。


3.使用GCD代替手动锁实现单例模式


4.使用宏封装直接便于开发使用

(1).懒汉模式

static id instance = nil;

// 懒加载 线程不安全 单例
+ (instancetype) ShareInstance
{
    if (instance == nil) {
        instance = [[self alloc] init];
    }
    return instance;
}

// 懒加载  加锁  单例
+ (instancetype) ShareInstance1
{
    @synchronized (self) { //为了线程安全,加上互斥锁
        if (instance == nil) {
            instance = [[self alloc] init];
        }
    }
    return instance;
}

1)加synchronized 是为了保证单例的读取线程安全,为什么需要添加synchronized

+(instancetype)sharedSingleton{
static id instance = nil;

  if (!instance) {
    @synchronized (self) {
      instance = [[self alloc] init];
    }
  }
  return instance;
}

肯定不行的,稍微对synchronized 有点了解就知道这种只是“锁”住了对象的创建,没有“锁”住 if 判断。如果两个线程都进到了 if 里面,一样可以生成两个对象。

2)static
修饰局部变量:

修饰了局部变量的话,那么这个局部变量的生命周期就和不加static的全局变量一样了(也就是只有一块内存区域,无论这个方法执行多少次,都不会进行内存的分配),不同的在于作用域仍然没有改变

修饰全局变量:

如果不适用static的全局变量,我们可以在其他的类中使用extern关键字直接获取到这个对象,可想而知,在我们所做的单例模式中,如果在其他类中利用extern拿到了这个对象,进行一个对象销毁,

extern id instance;
instance = nil;

这时候在这句代码之前创建的单例就销毁了,再次创建的对象就不是同一个了,这样就无法保证单例的存在,所以对于全局变量的定义,需要加上static修饰符

3) allocWithZone 与copyWithZone方法 

我们在项目中一般是直接调用自己定义的类方法:ShareInstance,但是有时候也会调用alloc方法直接对单例进行初始化,那么也会导致没有产生该单例,所以我们需要保证应用中只有一个该类的对象需要重写它的allocWithZone 方法,alloc调用的底层也是allocWithZone方法,直接与上述的单例方法类同。

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
        // 解决多线程问题
        @synchronized(self){
            if (instance == nil) {
                // 调用super的allocWithZone方法来分配内存空间
                instance = [super allocWithZone:zone];
            }
        }
    return instance;
}

如果使用copy创建出新的对象的话,那么就不能够保证单例的存在了也会导致同样的问题。此处直接返回instance就可以了。

- (id)copyWithZone:(NSZone *)zone
{
    return instance;
}

(2).饿汉模式 

在没有使用代码去创建对象之前,这个对象已经加载好了,并且分配了内存空间,当你去使用代码创建的时候,实际上只是将这个原本创建好的对象拿出来而已。在alloc之前如何将对象直接赋值呢,有两种方式:load和initialize。

load 会在类加载到运行环境中的时候就会调用且仅调用一次,同时注意一个类只会加载一次(类加载有别于引用类,可以这么说,所有类都会在程序启动的时候加载一次,不管有没有在目前显示的视图类中引用到)initialize方法:当第一次使用类的时候加载且仅加载一次

static id instance = nil;

+ (void)load
{
    instance = [[self alloc]init];
}

+ (void)initialize
{
    instance = [[self alloc]init];
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    if (instance == nil) {
        instance = [super allocWithZone:zone];
    }
    return instance;
}
+ (instancetype)sharedInstance
{
    return instance;
}
- (id)copyWithZone:(NSZone *)zone
{
    return instance;
}

实际上只需要实现 load 与 initialize 其中一种即可实现单例。 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值