iOS 单例模式

注意:以下都是ARC模式下操作,如果不幸是MRC模式,也比较简单,只需要写上四个方法让MRC代码失效,即可覆盖四个方法:分别是

//只需要写上这几句让MRC代码失效即可
- (oneway void)release{
}
- (instancetype)retain{
    return self;
}
- (NSUInteger)retainCount{
    return 1;
}
- (instancetype)autorelease{
    return self;
}
  • 单例模式有两种:懒汉式和饿汉式
  • 懒汉式是用到时再加载,饿汉式是程序启动加入内存的时候直接加载。
  • 具体如何创建以及为什么这么创建在代码注释中可以清晰看到
  • 其实都是重写三个方法:
  • 第一个肯定是alloc方法,只分配一次内存
  • 第二个是一个自定义的工厂模式的方法,我这里命名为sharedPlayer
  • 第三个是copy方法,以防止有时候用copy操作

1,不使用GCD的情况下:

懒汉式:
#pragma mark- 懒汉式加载方式

//static修饰时为了防止外部用extern进行引用这个类然后意外修改_musicPlayer的值。
static id _musicPlayer;

//单例模式实际上就是需要保证内存只有一个,而内存是在alloc方法中分配的,所以只需要在alloc方法中进行限制即可
//调用alloc的时候实际上是调用这个方法,所以最好在这个方法内部进行单例初始化
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    
    //判断是否需要加锁,如果需要,加上锁然后进行初始化,不然直接跳过,不必加锁,因为加锁比较消耗内存
    if (_musicPlayer == nil) {
        //如果前面什么都不判断,那么一进来就会加锁,加锁比较频繁,所以我们会在加锁之前先进行判断是否需要加锁
        @synchronized (self) {
            if(_musicPlayer == nil){
                //注意:这里一定是调用父类的方法
                _musicPlayer = [super allocWithZone:zone];
            }
        }
    }
    return _musicPlayer;
}

//一般建议写一个简便的全局方法进行初始化
//可以直接返回[[self alloc] init],但是这样虽然只会分配一次内存,但是每次仍然会初始化,不是最好,最好的方法如下
+ (instancetype)sharedPlayer{
    
    //这里应该是不需要加锁的,因为本质上这个方法里面还是调用alloc方法,在alloc的内部方法中已经加过锁,是安全的,这里就没必要加锁
    if (_musicPlayer == nil) {
        _musicPlayer = [[self alloc] init];
    }
    
    return _musicPlayer;
    
}
    
//有时候需要copy,所以需要重写这个方法
- (id)copyWithZone:(NSZone *)zone{
    
    //既然是拷贝,那么值钱必然已经创建过一次,所以可以直接返回这个全局变量即可
    return _musicPlayer;
}

饿汉式:
#pragma mark- 饿汉式加载方式
    
//static修饰时为了防止外部用extern进行引用这个类然后意外修改_musicPlayer的值。
static id _musicPlayer;
    
+ (void)load{
    
    //只会加载一次也就不需要加锁
    _musicPlayer = [[super alloc] init];
    
}
    
    
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    
    //不需要加锁,因为除了load方法中调用alloc的时候_musicPlayer是空之外,其他的时候都是有值的,直接返回_musicPlayer的
    if (_musicPlayer == nil) {
        _musicPlayer = [super allocWithZone:zone];
    }
    
    return _musicPlayer;
}
    
//一般建议写一个简便的全局方法进行初始化
//可以直接返回[[self alloc] init],但是这样虽然只会分配一次内存,但是每次仍然会初始化,不是最好,最好的方法如下
+ (instancetype)sharedPlayer{
    return _musicPlayer;
}
    
//有时候需要copy,所以需要重写这个方法
- (id)copyWithZone:(NSZone *)zone{
    return _musicPlayer;
}

2,使用GCD的情况下:

  • 其实就是把加锁判断的操作全部用dispatch_once方法进行修改就可以了
懒汉式:
#pragma mark- 使用GCD的懒汉式加载方式

//static修饰时为了防止外部用extern进行引用这个类然后意外修改_musicPlayer的值。
static id _musicPlayer;

//单例模式实际上就是需要保证内存只有一个,而内存是在alloc方法中分配的,所以只需要在alloc方法中进行限制即可
//调用alloc的时候实际上是调用这个方法,所以最好在这个方法内部进行单例初始化
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    
    //因为GCD默认是加过锁的,是线程安全的,所以不需要再做任何加锁判断操作了
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _musicPlayer = [super allocWithZone:zone];
    });
    
    return _musicPlayer;
}

//一般建议写一个简便的全局方法进行初始化
//可以直接返回[[self alloc] init],但是这样虽然只会分配一次内存,但是每次仍然会初始化,不是最好,最好的方法如下
+ (instancetype)sharedPlayer{

    //这里再调用一次GCD就OK了
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _musicPlayer = [[self alloc] init];
    });

    return _musicPlayer;

}

//有时候需要copy,所以需要重写这个方法
- (id)copyWithZone:(NSZone *)zone{
    //既然是拷贝,那么值钱必然已经创建过一次,所以可以直接返回这个全局变量即可
    return _musicPlayer;
}
饿汉式
  • 由于饿汉式是在加载的时候已经分配好空间,之后没有必要加锁判断,所以也就没有必要用GCD

你可能感兴趣的:(iOS 单例模式)