zoukankan      html  css  js  c++  java
  • 你研究过单例么?这样写单例效率最高.

    首先,小汤我在这里,要表示一下歉意,本来是想要每天写一篇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究竟是怎么实现的呢?


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

  • 相关阅读:
    “数学题”——传钱
    kafka笔记——入门介绍
    SpringBoot集成Dubbo+Zookeeper
    MySql基本语法
    动态规划
    总结
    Java反射
    软件清单
    Java IO操作
    Spring Boot AOP的使用
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/7044713.html
Copyright © 2011-2022 走看看