【iOS】单例模式

【iOS】单例模式

前言

笔者在之前已经写过有关于单例模式的相关博客,但是理解比较浅显,这里笔者重新学习有关于单例模式的一个内容

单例模式

定义

单例模式是一种创建类型的时候常用的一种设计模式,通过单例创建的类在整个程序进程中仅仅只存在一个实例。

如果说每一个人都是一个类,那么从他出生开始,他就是生活中的唯一实例,每当有人要拜访或者联系你的时候,无论别人认识你的时候你是什么状态,他所能联系到的都是现在的你。你本身的状态会在其他地方发生改变,即当你状态改变后,后续所有找到你的,都是看到状态改变后的你。那么,我们就可以认为每一个人都处于单例模式。

在开发中我们其实以及认识到一些单例了,如UIApplication,通知中心以及NSUserDefaults,这些都是在整个iOS开发中仅仅具有唯一实例的内容。

应用场景

当你需要在整个程序的运行周期中,让一个类始终保持同一个实例。则必须使用单例模式;(例如 [UIApplication sharedApplication]

在生命周期中管理数据,例如自定义的管理中心或者[NSUserDefaults standardUserDefaults]

作用

  • 在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在APP开发中我们可能在任何地方都要使用用户的信息,那么可以在登录的时候就把用户信息存放在一个文件里面,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理
  • 有的情况下,某个类可能只能有一个实例。比如说你写了一个类用来播放音乐,那么不管任何时候只能有一个该类的实例来播放声音。再比如,一台计算机上可以连好几个打印机,但是这个计算机上的打印程序只能有一个,这里就可以通过单例模式来避免两个打印任务同时输出到打印机中,即在整个的打印过程中我只有一个打印程序的实例

注意点

  • 创建一个单例的时候,注意要保证我们在多线程的环境下以及在alloccopy以及mutablecopy这几个状态下返回的单例要保持一致。

单例模式的种类

懒汉式

懒汉式创建单例模式的意思其实就是指我们在创建的时间是我们需要用到这个单例的时候,我们才开始创建这个唯一实例,这种创建模式有助于提高性能,以及节省资源的效果。简单来说,就是我们平时日常生活中的deadline,只要在达到deadline的时候我们才去提交工作,这和他的名字也很相似,懒汉式延迟创建

相关代码

我们在之前说过创建单例要注意多线程环境以及让alloccopymutablecopy这三个方法返回的单例保持一致。所以就意味着我们需要重写的方法有以下几个:

+ (instancetype)allocWithZone:(struct _NSZone *)zone
- (id)copyWithZone:(NSZone *)zone
-(id)mutableCopyWithZone:(NSZone *)zone

我们重写单例模式的方法大致一共有两种,一种是GCD写法,另一个则是通过添加一个锁来实现一个单例

GCD写法
#import "sharedSingleton.h"
static id _instance;
@implementation sharedSingleton
+(instancetype)sharedSingleton {
    _instance = [[self alloc] init];
    return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone {
    return _instance;
}
@end

dispatch_once 主要是根据 onceToken 的值来决定怎么去执行代码。
1.当 onceToken = 0 时,线程执行 dispatch_once 的 block 中代码;
2.当 onceToken = -1 时,线程跳过 dispatch_once 的 block 中代码不执行;
3.当 onceToken 为其他值时,线程被阻塞,等待 onceToken 值改变。
当线程调用mySingleton方法时,此时 onceToken = 0,调用 block 中的代码,此时 onceToken =其他值。
当其他线程再调用 mySingleton 方法时,onceToken为其他值,线程阻塞。当 block 线程执行完 block之后,onceToken = -1,其他线程不再阻塞,跳过 block。下次再调用mySingleton方法 时, block 已经为-1,直接跳过 block。转载自【iOS】—— 单例模式

互斥锁写法
#import "sharedSingleton.h"
static id _instance;
@implementation sharedSingleton
+(instancetype)sharedSingleton {
    if (_instance == nil) { // 防止一个频繁加锁
        @synchronized (self) {
            if (_instance == nil) {
                _instance = [[super alloc] init];
            }
        }
    }
    return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    //降低读取时的一个卡顿,防止一个频繁加锁
    if (_instance == nil) {
        @synchronized (self) {
            if (_instance == nil) {
                _instance = [super allocWithZone:zone];
            }
        }
    }
    return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone {
    return _instance;
}
@end

这里我们需要讨论一个有关锁的位置的问题

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    //降低读取时的一个卡顿,防止一个频繁加锁
  	if (_instance == nil) {
        @synchronized (self) {
            _instance = [super allocWithZone:zone];
         }
    }
    return _instance;
}

如果是这样子去创建一个单例的话会出现问题,因为这种创建方式允许我们创建的时候两个线程同时进入判断,这样就会创建两个单例。

所以这种创建单例的方式是错误的。

饿汉式

饿汉式创建单例则是在你的类加载的时候立刻就开始加载一个单例的一种单例模式,这种模式我们需要把我们的加载单例的代码写在类第一次加载的位置。

#import "sharedSingleton.h"
static id _instance;
@implementation sharedSingleton
+ (void)load { // 在类加载到OC运行时的环境内存中,就会调用这部分内容
    _instance = [[self alloc] init];
}
+(instancetype)sharedSingleton {
    return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    if (!_instance) {//防止一个多次创建
        _instance = [super allocWithZone:zone];
    }
    return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone {
    return _instance;
}
@end

各自优点和缺点

  • 懒汉式:
    • 优点:
      1. **延迟加载:**只有在第一次访问单例实例的时候才会创建,这样会有一个节省资源,提高性能的效果
      1. 线程安全性:我们的懒汉式加载可以通过GCD或者加锁来保证一个线程安全性
    • 缺点:
      1. 线程安全性的开销:为了保证线程安全,我们需要引入其他的代码来保证这次操作的一个线程安全性,会带来一些性能上的开销
  • 饿汉式:
    • 优点:
      1. 创建方式简单:饿汉式的创建比较简单,不会出现一些多线程的问题
      2. 线程安全:实例在类创建的时候就会创建,可以保证一个线程的安全性
    • 缺点:
      1. 性能开销比较大:我们如果一个程序的初始化程序很大的时候,我们如果再次采用这种方式创建会造成比较严重的性能开销问题。

小结

总的来说,懒汉模式适用于需要延迟加载实例的情况,可以节省资源和提高性能,但需要考虑线程安全性。饿汉模式适用于需要简单实现和线程安全性的情况,但不支持延迟加载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值