iOS开发笔记之八十——单例的使用总结笔记

******阅读完此文,大概需要10分钟******

一、单例的创建

#import "MDInstanceManager.h"
 
@implementation MDInstanceManager
 
static MDInstanceManager *shareInstance = nil;
static dispatch_once_t onceToken;
 
+ (instancetype)shareInstance
{
    dispatch_once(&onceToken, ^{
        shareInstance = [[self alloc] init];
    });
    return shareInstance;
}
  
- (instancetype)init
{
    self = [super init];
    if (self) {}
    return self;
}
@end

单例的关键字static,被它修饰的对象,存储在静态存储区,会在当前内存中保持唯一性和持久性(对象会在程序的整个运行期间都存在);上面的单例写法是最为常见的做法,dispatch_once保证了线程安全性,但是如果要保证真正的唯一性,还不够。

二、“严格”的单例

+ (id)allocWithZone:(NSZone *)zone
{
    if(!shareInstance) {
        shareInstance = [super allocWithZone:zone];
    }
    return shareInstance;
}
 
- (id)copy
{
    return shareInstance;
}
 
- (id)mutableCopy
{
    return shareInstance;
}

创建对象的步骤分为两步:(1)申请内存(alloc);(2)初始化(init) 这两个步骤;

因为外部的操作是不可控的,为了保证严格的唯一性,因此在第一步这个阶段我们就要拦截它;当我们调用alloc方法时,OC内部会调用allocWithZone这个方法来申请内存,我们覆写这个方法,然后在这个方法中调用shareInstance方法返回单例对象,这样就可以达到我们的目的。

拷贝对象也是同样的原理,覆写copy方法,然后在这个方法中调用shareInstance方法返回单例对象。

这样以来:

MDInstanceManager *instanceManager = [MDInstanceManager shareInstance];
NSLog(@"-->%@",instanceManager);
NSLog(@"-->%@",[[MDInstanceManager alloc]init]);
NSLog(@"-->%@",[instanceManager copy]);
NSLog(@"-->%@",[instanceManager mutableCopy]);
2020-06-30 11:21:53.886952+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>
2020-06-30 11:21:53.887055+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>
2020-06-30 11:21:56.200656+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>
2020-06-30 11:21:56.840126+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>

以上四种操作的内存地址都是一样的;

当然你也可以采取更为粗暴的方法,如下:

- (instancetype)init __attribute__((unavailable("replace with 'sharedInstance'")));
+ (instancetype)alloc __attribute__((unavailable("replace with 'sharedInstance'")));
+ (instancetype)new __attribute__((unavailable("replace with 'sharedInstance'")));
- (instancetype)copy __attribute__((unavailable("replace with 'sharedInstance'")));
- (instancetype)mutableCopy __attribute__((unavailable("replace with 'sharedInstance'")));

三、单例优缺点

优点:

  • 提供了应用唯一的实例对象,规范化统一管理资源,即提供了对唯一实例的受控访问; 

  • 不用再频繁地创建和销毁对象,从而提高了系统的性能和节约系统资源;

  • 单例对象可以做到按需创建对象或加载资源,以节省不必要的内存;

  • 避免对共享资源的多重占用;

缺点:

  • 单例从创建后到彻底关闭程序前都会一直存在,如果过多的创建单例无疑浪费系统资源和影响系统效率;

  • 由于单例模式中没有抽象层接口,因此单例类很难再进行扩展;

  • 单例类的职责过重,在一定程度上违背了“单一职责原则”,长期的累积会导致难以维护;

  • 因此可以把单例模式作为最后的兜底方案考虑,不宜过多使用;

四、单例的销毁

单例是可以销毁的,如下:

- (void)destroy
{
    shareInstance = nil;
    onceToken = 0;
}

五、Weak单例


static MDInstanceManager *shareInstance = nil;
以上static修饰的默认用一个强指针来持有这个单例,如果当前业务场景退出后,对应的单例管理对象,也没有用了,当然,也可以手动释放这个单例对象,但是业务使用中,需要注意的是释放的时机;
static __weak MDWeakInstanceManager *weakInstance = nil;
但是,如果改用OC特有的weak修饰,让对应的VC去持有它,那么这个weak单例就会自动伴随对应的VC释放而释放,而不需要我们去手动在干预;对于复杂的业务场景,我们常常需要一个管理类来作为中间者,weak单例无疑是一种很好的选择。

为了规范化这个过程,在实际开发中,我定义了一个protocol才实现VC对weak单例的持有,让对应的持有者去实现它,如下:
@protocol MDWeakInstanceManagerDelegate <NSObject>
 
- (void)assignInstance:(MDWeakInstanceManager *)instance;
 
@end

持有者VC的实现代码如下:

@interface MDWeakInstanceViewController ()<MDWeakInstanceManagerDelegate>
 
@property (nonatomic, strong) MDWeakInstanceManager *weakInstanceManager;
  
@end
  
@implementation MDWeakInstanceViewController
  
- (void)viewDidLoad
{
    [super viewDidLoad];
    [MDWeakInstanceManager buildInstance:self];
}
  
#pragma mark -- MDWeakInstanceManagerDelegate
 
- (void)assignInstance:(MDWeakInstanceManager *)instance
{
    self.weakInstanceManager = instance;
}
  
@end

单例中实现如下:

static __weak MDWeakInstanceManager *weakInstance = nil;
  
+ (void)buildInstance:(id)delegate;
{
    MDWeakInstanceManager *strongInstance = weakInstance;
    @synchronized(self) {
        if (!strongInstance) {
            strongInstance = [[[self class] alloc] init];
            weakInstance = strongInstance;
        }
    }
    strongInstance.delegate = delegate;
    if (strongInstance.delegate && [strongInstance.delegate respondsToSelector:@selector(assignInstance:)]) {
        [strongInstance.delegate assignInstance:strongInstance];
    }
}
 
//访问时须用此方法
+ (MDWeakInstanceManager *)shareInstance
{
    return weakInstance;
}

这样我们在持有者VC的业务场景中,都可以使用 [NMLKPBMusicManager shareInstance],而一旦它的持有者VC释放了,[NMLKPBMusicManager shareInstance]也会自动变为nil,无需手动干涉;

 

参考文献

https://www.cnblogs.com/NerdFooProgrammer/p/4870260.html

https://www.yunbook.vip/post/1547606575302.html

http://www.cocoachina.com/articles/19857

 

请关注公众号:

热门文章

暂无图片
编程学习 ·

Centos7下为Open-falcon部署OpenTsdb

一边安装一边写,写得略乱安装OpenTsdb需要HBase,HBase需要Zookeeper和HDFS这一串都是apache家的,安装java是必须的yum install -y java一、安装Zookeeper在五个节点上部署zk下载tar包https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.6.1/apache-zookeep…
暂无图片
编程学习 ·

利用BootStrap创建搜索框--」详解

今天学了bootstrap由于官网上没有搜索框,我要做一个网站正好需要,我就自己做了一个搜索框,话不多说直接上代码下面是jsp代码<div class="col-sm-5" id="so"><div class="input-group"><input type="text" class="…
暂无图片
编程学习 ·

Spring Boot / Spirng Cloud 引入Rabbit MQ

注意: spring cloud版本:Greenwich.RELEASE spring boot 版本: 2.1.5.RELEASE 1.导包,在pom.xml中导入<dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-rabbit</artifactId></dependency>2.加入配置文件 …
暂无图片
编程学习 ·

Docker 2375 端口入侵服务器,部分解决方案

docker remote API的同学对2375端口入侵服务器2375->上传镜像-》获取控制权-》ssh pub key 注入-》登入服务器核心总结:1.禁用2375 2.创建linux新用户 3.禁止root远程登录4.卸载重新安装docker,并删除之前的文件5.禁止外网一、创建新用户以及授权创建用户adduser limp用户…
暂无图片
编程学习 ·

ps如何实现阳光照射效果

1.ps打开图片。2.按ctrl+j复制背景图层就会自动创建图层(图层 0 副本),选择图层 0 副本,按ctrl+alt+2调出高光选区,再按ctrl+j复制高光选区就会自动创建图层(图层 1)。3.选择图层1,ctrl+L打开色阶工具,拖动输入色阶的右边白色滑块向左边移动把图中亮的的地方调更亮。4…
暂无图片
编程学习 ·

Shiro框架简单使用

文章目录1. Shiro过滤器&标签简介Shiro过滤器Shiro的JSP标签2. Shiro登陆认证(一)使用认证过滤器目标实现3. Shiro登陆认证(二)完成登录认证(*)目标总结4. Shiro登陆认证(三)凭证匹配器-普通加密需求步骤总结5. Shiro登陆认证(四)凭证匹配器-加盐加密需求什么是加…
暂无图片
编程学习 ·

V2Pro春季班普遍学撑了,秋季班7月报名你还敢来么

今年V2春季班开班以后,同学们的学习信心保持着高涨,一直维持到了课程结业。今年春季班课程学习的进度在V2.1模块结束以后,我们开始推出了当时号称“加量不加价”的V2Pro增量课程,从DVT Eclipse工具的使用、覆盖率驱动验证管理流程、TBA测试平台自动化初级应用、寄存器一致性…
暂无图片
编程学习 ·

新能源汽车车载智能终端T-BOX

新能源汽车车载智能终端T-BOX技术解决方案包括:用户端 + 运营后台系统 + 车辆网平台 + 车载终端等总体架构:“端+网”解决方案智能信TBOX终端硬件,TBOX终端与汽车共享、网约及分时租赁商业应用相结合,实现远程开锁,GPS追踪、动力控制管理、闪灯鸣笛寻车等功能。同时,提供…
暂无图片
编程学习 ·

Tomcat 启动控制台乱码

Tomca 启动控制台乱码将tomcat用作web应用服务器,在tomcat的服务迭代中,服务漏洞是不可避免需要升级的来修复漏洞的。在修复漏洞的时候通常需要将原来的webapps下的文件复制到新tomcat中,并替换tomcat/conf/server.xml文件(小版本升级都可以用这种方式)。解决乱码 在tomca…
暂无图片
编程学习 ·

迭代器

概念 提供对对象的间接访问,有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置 使用 获取 begin()返回指向第一个元素/字符的迭代器 end()返回指向容器(或string)尾元素的下一位置即根本不存在的尾后元素解引用 如it为vector对象的迭代器:(*it).empty()或者it-&…
暂无图片
编程学习 ·

专业外语学科复习总结

文章目录英译汉选择题Unit 1-ExcerciseUnit 2-ExerciseUnit 3-ExerciseUnit 4- ExerciseUnit 5- ExerciseUnit 6- ExerciseUnit 7- Exercise阅读题作文阶段性巩固练习(unit1~3)阶段性巩固练习(unit4~5)阶段性巩固练习(unit6~7) 英译汉 OS also manage files on computer hard d…
暂无图片
编程学习 ·

Java 实现获取当前时间的前一个月

public class afterTime {public static void main(String[] args) {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("当前时间"+df.format(new Date()));//1代表为一个月 正的往前 负的为后Date date = step…
暂无图片
编程学习 ·

将word文档锁定其他用户不可编辑

将word文档锁定其他用户不可编辑 首先打开你需要锁定的word文档然后点击审阅,如上图 点击限制编辑 点击现在对选定的样式设置格式化,防止被格式化 点击仅允许在文档中进行此类型的编辑 在下拉框内选择不允许任何修改(只读) 点击"是,启动强制保护"
暂无图片
编程学习 ·

什么是python?python有什么用途?

新手哪门编程语言最合适?绝对是python。python是目前主流的编程语言,也是当下发展最为迅速的编程语言,python可以做很多事情,无论是入门新手还是专业级选手都可以使用python。python是什么?有什么用途?python是一门非常具有条理、强大的面向对象的程序设计语言,类似于Pe…
暂无图片
编程学习 ·

微信小游戏订阅消息

微信官方接口: 点击这里 wx.requestSubscribeMessage(Object object) 注意事项 一次性模板 id 和永久模板 id 不可同时使用。 低版本基础库2.4.4~2.8.3 已支持订阅消息接口调用,仅支持传入一个一次性 tmplId / 永久 tmplId。 2.8.2 版本开始,用户发生点击行为或者发起支付回…
暂无图片
编程学习 ·

iOS 渐变色View

通过 CAGradientLayer 实现UIView * gradationView = [[UIView alloc]initWithFrame:CGRectMake(100, 100, 100, 50)]; [self.view addSubview:gradationView];CAGradientLayer *gradientLayer = [CAGradientLayer layer]; gradientLayer.frame = gradationView.bounds; // 渐变…