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

首先,小汤我在这里,要表示一下歉意,本来是想要每天写一篇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.184217
    2015-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.089754
    2015-06-06 15:04:58.763 test[1125:28301] ====方法2耗时:0.065470  

结果是不是非常惊讶?! 我 也 表 示 非常 吃 惊 !

没有想到小汤我写的单例的效率竟然比dispatch_once的效率还要略高那么一丝.

当然,这个仅仅是让小汤我稍微嘚瑟了一下,重点是,小汤我还是没想清楚,GCD下的这个dispatch_once究竟是怎么实现的呢?

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

时间: 2024-10-14 00:30:44

你研究过单例么?这样写单例效率最高.的相关文章

【Swfit】Swift与OC两种语法写单例的区别

Swift与OC两种语法写单例的区别 例如写一个NetworkTools的单例 (1)OC写单例 1 + (instancetype)sharedNetworkTools { 2 static id instance; 3 4 static dispatch_once_t onceToken; 5 6 dispatch_once(&onceToken, ^{ 7 instance = [[self alloc] init]; 8 //这里可以做一些初始化 9 }); 10 11 return i

iOS使用宏写单例

本文只介绍ARC情况下的单例 过去一直背不下来单例如何写,就是知道这么回事,也知道通过宏来写单例,但是一直记不住,今天就来记录一下 - (void)viewDidLoad {     [super viewDidLoad];     SIPerson *person = [[SIPerson alloc] init];    NSLog(@"%@",person);     SIPerson *person1 = [[SIPerson alloc] init];    NSLog(@&

手写单例singleton

1 // 2 // ViewController.m 3 // singleton 4 // 5 // Created by ys on 15/11/23. 6 // Copyright (c) 2015年 ys. All rights reserved. 7 // 8 9 10 #import "ViewController.h" 11 #import "singletonClass.h" 12 13 @interface ViewController () 14

转--python 中写单例

原文地址 原文地址2 Python中的单例模式的几种实现方式的及优化 阅读目录(Content) 单例模式 实现单例模式的几种方式 1.使用模块 2.使用装饰器 3.使用类 4.基于__new__方法实现(推荐使用,方便) 5.基于metaclass方式实现 相关知识 实现单例模式 回到顶部(go to top) 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对

struts2的action是多例,servlet是单例

struts2中action是多例的,即一个session产生一个action如果是单例的话,若出现两个用户都修改一个对象的属性值,则会因为用户修改时间不同,两个用户访问得到的 属性不一样,操作得出的结果不一样.举个例子:有一块布长度300cm,能做一件上衣(用掉100cm)和一件裤子(用掉200cm);甲和乙同时访问得到的 长度都是300cm,甲想做上衣和裤子,他先截取100cm去做上衣,等上衣做完再去做裤子,而乙这时正好也拿100cm去做上衣,那 好,等甲做完上衣再做裤子的时候发现剩下的布(

单例模式(懒汉式单例、饿汉式单例、登记式单例)

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建.这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象. 注意: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 介绍 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点.

Spring单例Bean中注入多例Bean

问题: 当在一个单例Bean中注入一个多例Bean的时候,是获取不到那个多例对象的,因为,单例在初始化的时候,就直接初始化,这个多例Bean啦, 一直获取的是第一次初始化的Bean 配置文件: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="htt

设计模式中饿汉式单例类和懒汉式单例类

单例模式的特点为: *单例类只有一个实例. *单例类必须自己创建自己的唯一实例. *单例类必须给所有其他对象提供这一实例. 饿汉式单例类: 而饿汉式单例类则在java语言中是最为简单的单例类,这是一个描述饿汉式单例类的类图的实现. 此类图中,此类已经将自己实例化. 源码为: package singleton; public class EagerSingleton { private static EagerSingleton instance=new EagerSingleton(); /*

Scala 深入浅出实战经典 第79讲:单例深入讲解及单例背后的链式表达式

package com.parllay.scala.bestpractive import scala.reflect.runtime.universe import scala.reflect.runtime.universe.typeOf import com.parllay.scala.bestpractive.Scala /** * Created by richard on 15-9-5. * 第79讲:单例深入讲解及单例背后的链式表达式 */ object Scala class J