首先,小汤我在这里,要表示一下歉意,本来是想要每天写一篇Swift的学习小tip的,无奈近期手头的money花差的差点儿相同了,仅仅能迫不得已,出门找工作去了,没能履行承诺之处还请大家见谅.

那么,废话不多说了,開始我们今天的主题: 单例 !

单例介绍:

说到单例,大家应该都不陌生,在传说中的那23种 (为啥我就会6种捏o(╯□╰)o…) 设计模式中,单例应该是属于和简单工厂模式并列的最简单的设计模式了,也应该是最经常使用的.

像这样简单易懂,又能有效提高程序执行效率的设计模式,作为一个iOS程序猿,必定是十分熟练的啦.

今天啊,小汤我就给大家介绍一下在Objective-C中,我们经常使用的单例模式的写法,以及小汤我在研究当中某种写法时,写出来的一个效率更高的写法.

当然啦,MRC下的写法,我就不多说了,已经有那么多大牛写过了,我就简化一下,直接写在ARC下的写法啦,MRC能够直接把相关代码套用过去即可喽~

网上流传的Objective-C的单例写法:

    + (instancetype)sharedPerson0{static id instance0 = nil;static BOOL once0 = YES;@synchronized(self){if (once0) {instance0 = [[Person alloc]init];once0 = NO;}}return instance0;}
以上就是网上流传已久的单例模式的写法啦.

通过GCD实现的单例模式写法:

    + (instancetype)sharedPerson1{static id instance1 = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{instance1 = [[self alloc]init];});return instance1;}
这是GCD方式,也就是使用dispatch_once实现的单例模式的写法.

首先展示一下两者是不是都实现了单例呢?为此,小汤我新建了一个Person类,在当中实现了这两种方法,然后在控制器启动的时候执行了以下两段代码

    for (int i = 0; i < 10; i++) {NSLog(@"--单例方法0:%@",[Person sharedPerson0]);}NSLog(@"-----");for (int i = 0; i < 10; i++) {NSLog(@"--单例方法1:%@",[Person sharedPerson1]);}NSLog(@"-----");

执行结果例如以下:

    2015-06-06 14:46:35.906 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.907 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.907 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.907 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.907 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.907 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.907 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.907 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.907 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.908 test[966:22855] --单例方法0:<Person: 0x7f9c19418740>2015-06-06 14:46:35.908 test[966:22855] -----2015-06-06 14:46:35.908 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.908 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.908 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.908 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.908 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.908 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.960 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.960 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.960 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.960 test[966:22855] --单例方法1:<Person: 0x7f9c1961e510>2015-06-06 14:46:35.960 test[966:22855] -----

能够看到这两种方式写的单例模式都是能够实现我们的需求的.
那么两者有什么差别呢?
以下我们来看一看两者在执行时间上的差别:

    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();for (int i = 0; i < 1000000; ++i) {[Person sharedPerson0];}NSLog(@"====方法0耗时:%f",CFAbsoluteTimeGetCurrent() - start);start = CFAbsoluteTimeGetCurrent();for (int i = 0; i < 1000000; ++i) {[Person sharedPerson1];}NSLog(@"====方法1耗时:%f",CFAbsoluteTimeGetCurrent() - start);

我通过上面这两个方法,比較两个单例模式在分别实例化100万个对象的耗时,结果例如以下:

    2015-06-06 14:50:47.899 test[1009:24267] ====方法0耗时:0.1842172015-06-06 14:50:47.981 test[1009:24267] ====方法1耗时:0.081377

能够看到,方法1的耗时明显要少于方法二的耗时,那么为什么GCD能够做到这一点呢?
小汤思考之后,认为应该是@synchronized这个锁对性能的消耗十分明显.

而在打印了dispatch_once这种方法的入參onceToken之后,发现,在实例化这个对象之前,onceToken的值为0,而之后变为-1.

于是,在这个基础上,小汤我想到了一个方法来降低这样的性能消耗.

那么问题来了?

dispatch_once会是通过小汤我想象的这样做的么?

小汤我的单例实现:

    + (instancetype)sharedPerson2{static id instance2 = nil;static BOOL once2 = YES;static BOOL isAlloc = NO;if (!isAlloc) {@synchronized(self){if (once2) {instance2 = [[Person alloc]init];once2 = NO;isAlloc = YES;}}}return instance2;}

我在进行同步锁之前,再进行了一次推断,这样会导致什么后果呢?

非常显然,因为内部有相互排斥锁,那么在实例化对象时,肯定仅仅有一个对象被实例化,然后在实例化对象之后,因为内部存在一个推断,那么就不会再有其它的对象被实例化,而在外面的这个推断,又能在下一次外部变量进行訪问的时候直接返回值,提高了效率.

说了那么多,先来測试一下小汤我的代码是不是能够创建一个单例呢?

測试代码:

    for (int i = 0; i < 10; i++) {NSLog(@"--单例方法2:%@",[Person sharedPerson2]);}NSLog(@"-----");

測试结果:

    2015-06-06 15:01:40.412 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.412 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.412 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.412 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.412 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.413 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.413 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.413 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.413 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.413 test[1081:26913] --单例方法2:<Person: 0x7fd891553e20>2015-06-06 15:01:40.413 test[1081:26913] -----

以上结果能够显示,小汤我的单例也是可行的.那么我们来对照一下我的这个实现和GCD的那种实现方式是不是一样呢?

效率測试代码:

    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();for (int i = 0; i < 1000000; ++i) {[Person sharedPerson1];}NSLog(@"====方法1耗时:%f",CFAbsoluteTimeGetCurrent() - start);start = CFAbsoluteTimeGetCurrent();for (int i = 0; i < 1000000; ++i) {[Person sharedPerson2];}NSLog(@"====方法2耗时:%f",CFAbsoluteTimeGetCurrent() - start);

还是比較100万次,我们来看看效率怎样呢?
測试结果:

    2015-06-06 15:04:58.696 test[1125:28301] ====方法1耗时:0.0897542015-06-06 15:04:58.763 test[1125:28301] ====方法2耗时:0.065470  

结果是不是非常惊讶?! 我 也 表 示 非常 吃 惊 !
没有想到小汤我写的单例的效率竟然比dispatch_once的效率还要略高那么一丝.
当然,这个仅仅是让小汤我稍微嘚瑟了一下,重点是,小汤我还是没想清楚,GCD下的这个dispatch_once究竟是怎么实现的呢?

是不是还存在优化的可能呢?希望对此有研究的各位大牛给个答案哈~

你研究过单例么?这样写单例效率最高.相关推荐

  1. 单例设计模式-ThreadLocal线程单例

    package com.learn.design.pattern.creational.singleton;/*** 是基于ThreadLocal的* 所以必不可少要使用这个类* * 我们看一下这个类 ...

  2. iOS严谨单例写法/可继承单例

    单例模式在iOS开发中可能算是最常用的模式之一了,但是由于OC本身的语言特性,想要写一个正确的单例模式相对来说比较麻烦. 今天就来说一说, 单例创建的方式和严谨的单例写法及可继承单例编写. 基本单例的 ...

  3. python线程安全的单例_[python笔记] 单例的几种实现方式(线程安全)

    单例实现方式: 1.单例装饰器; 2.使用类: 3.使用元类 1.单例装饰器(非线程安全) 通过装饰器使一个类变成单例类,但是并非线程安全的,多线程状态下,每个线程创建不同的实例,因为没有加线程锁,所 ...

  4. 手写识别底层原理_LinkedList底层原理和手写单链表

    2.1 单链表技能点 · 认识单链表 o 特点 数据元素的存储对应的是不连续的存储空间,每个存储结点对应一个需要存储的数据元素. 每个结点是由数据域和指针域组成. 元素之间的逻辑关系通过存储节点之间的 ...

  5. java中单例设计模式登记式单例类_java23种设计模式-创建型模式之单例模式

    单例模式(Singleton) 单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处: 1.某些类创建比较频 ...

  6. 用java写注册表单_利用HTML表单标签编写一个注册页面

    今天我们来写一个注册页面 form表单 先来利用表单标签制作一个简单的注册页面,给大家说说标签的结构: 页面结构大体就是这样子的~ 利用HTML表单标签编写一个注册页面 表单标签: 所有需要提交到服务 ...

  7. c# 多线程单例模式_单例模式,多线程单例,双重锁定单例,工场单例创建上下文...

    单例模式,多线程单例,双重锁定单例,工厂单例创建上下文. 单例子模式定义 保证一个类仅有一个实例,并提供一个访问它的全局访问点. 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个 ...

  8. java servlet是单例吗_Servlet是单例的吗?

    如题,是吗?首先我们得搞清楚啥是单例.一聊起单例,条件反射的第一个想到的自然是单例模式.单例模式的定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供.如果按照Java中单例的定义,那么当Ser ...

  9. 怎么设计接口测试用例更好——百度大佬“教你写用例”

    一.简介 在开始接口测试之前,我们想一下,接口测试的流程是什么?说到这里,有些人就会产生好奇和疑问,心里mmp:接口测试要什么流程哈???不就是参考接口文档,直接利用接口测试工具(例如jmeter和p ...

最新文章

  1. linux调试C++错误: 程序中有游离的‘\240’‘\302’
  2. Google 深度学习笔记 - Limit of Linear Model
  3. PHP内存管理 垃圾回收
  4. JavaWeb-SpringBoot(抖音)_二、服务器间通讯
  5. 通过SSH连接远程Jupyter Notebook
  6. 21世纪C语言(影印版)
  7. Java魔法堂:解读基于Type Erasure的泛型
  8. 『摄影欣赏』好萌了!12幅可爱的婴儿照片【组图】
  9. EventUtil——跨浏览器的事件对象
  10. 用两个队列实现一个栈
  11. 字符图形自动生成(C语言)
  12. java编程比赛_[阶段一]java基础编程比赛
  13. cmd命令跳舞代码_Golang语言元编程之代码生成
  14. Linu修改系统时间
  15. 分门别类刷leetcode——链表(C++实现)
  16. harmonyos系统官网,harmonyos系统官网2.0版本
  17. html三栏式布局图,div布局的自由伸展三栏式版面
  18. 字符串里面的\u200b
  19. 光电开关的工作原理与分类
  20. springboot学生会管理系统、

热门文章

  1. 李宏毅机器学习课程-Structured Learning
  2. 肺部胸片图像掩膜和伪彩色处理matlab
  3. 基于Python语言使用RabbitMQ消息队列(一)
  4. java基础-泛型举例详解
  5. Win10安装MySQL5.7.22 解压缩版(手动配置)方法
  6. xml的方式配置AOP:Aspect Oriented Programming
  7. Linux高级编程--01.vi命令
  8. 数据结构(复习)--------关于平衡二叉树(转载)
  9. 神经网络中的反向传播算法
  10. C++程序设计教程学习(1)-第一部分 编程基础