• 【设计模式】五、单例模式(独一无二的对象)


    一、概述:

    有一些对象我们只需要一个,比方说:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置和注册表对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。事实上这些对象只需要一个实例,如果制造出多个实例就会导致很多问题发生。(利用静态类变量、静态方法和适当的访问修饰符的确也可以做到只存在一个实例。)

    苏格拉底诱导式回答:(参考《Head First 设计模式》)

    如何创建一个对象? new MyObject()
    万一另外一个对象想创Myobject会怎样?可以再次new MyObject吗? 是的,当然可以。
    所以一旦有一个类,我们是否都能多次的实例化它? 如果是公开的类 就可以
    如果不是的话,会怎样?

    如果不是公开的类,只有同一个包内的类可以实例化它,但是仍可以实例化它很多次

    可以这么做吗?

    public Myclass
    {
        private Myclass(){}
    }
     我没想过,但是这是合法的定义,有一定的道理。
     怎么说呢?  我认为含有私有的构造器类不能呗实例化。
     又可以使用私有的构造器对象吗?  恩,我想Myclass内的代码是唯一能调用此构造器的代码,但是这又不太合乎常理。
     WHY?  因为只有Myclass的实例才能调用Myclass的构造器,但是因为没有其他类能够实例化Myclass,所以我们得不到这样的实例。

     嘿,我有个想法。

    你认为如何?

    public Myclass
    {
      public static MyClass getInstance(){}
    }

    MyClass有一个静态方法,我们可以这样调用这个方法:

    MyClas.getInstance();

     
     为何调用的时候用MyCLass类名,而不是对象名。  因为getInstance是类方法,是一个静态方法,你需要使用类名。

     有意思,假如把这些合在一起“是否”就可以初始化一个MyClass?

    public MyClass
    {
      private MyClass(){}
      public static Myclass getInstance()
       {
            return new MyClass();
       }
    }
    当然可以 
     好了,你能想出第二种实例化对象的方式吗?  MyClass.getInstance();
     你能够完成代码是MyClass只有一个实例被产生吗?  恩,大概可以吧。。。。

    单例模式优点

    1. 正如前面说的,单例模式在内存中只有一个实例,减少了内存开支。特别是一个对象需要频繁的创建、销毁时,而创建与销毁的性能有无法优化,单例模式的优势就非常明显。
    2. 单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
    3. 单例模式可以避免对资源的多重占用。
    4. 单例模式可以在系统设置全局的访问点,优化和共享资源访问。

    单例模式缺点

    1. 单例模式一般没有接口,扩展很困难,除了修改代码基本上没有第二种途径实现。
    2. 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的。
    3. 单例模式与单一职责原则有冲突。

    二、在IOS中的应用

    单例模式在iOS开发中的使用还是蛮多的,许多FoundationCocoaUIKit中的类都实现了单例模式,比如应用程序本身UIApplication、文件操作类NSFileManager、消息中心NSNotificitonCenter等系统都已经给我们实现单例,我们只需要使用就好了。在iOS中使用单例模式要使用类方法,通过类方法返回该类的唯一对象。

    在ios中的应用主要有以下三种方式

    1、

    static Singleton *instance = nil;
    
    + (Singleton *)sharedInstance
    {
        if (instance == nil) {
            instance = [[super allocWithZone:NULL] init];
        }
        return instance;
    }
    
    + (id)allocWithZone:(NSZone *)zone
    {
        return [[self sharedInstance] retain];
    }
    
    - (id)copyWithZone:(NSZone *)zone
    {
        return self;
    }
    
    - (id)retain
    {
        return self;
    }
    
    - (NSUInteger)retainCount
    {
        return NSUIntegerMax;  //denotes an object that cannot be released
    }
    
    - (void)release
    {
        //do nothing
    }
    
    - (id)autorelease
    {
        return self;
    }

    可以看到这种方式,使用静态成员维持了一个永久存在的对象,而且覆盖了alloc方法(alloc方法会调用allocWithZone:方法),并且也覆盖了所有与引用技术有关的方法,这都使这个对象不会被销毁。这样看上去基本实现了我们需要的,但是写起来麻烦不说,还有很大的一个问题,那就是多线程问题,如果是在多线程中那么该种方法就不能保证只产生一个对象了。所以这种方式只是介绍一下,并不推荐使用。

    2、引入头文件

    程序员都是偷懒的,现在流行使用一个宏定义来搞定这许多的事,而且考虑的更加周全。

    单例包含以下接口 

    + (MyClass*) sharedInstance; 
    + (void) purgeSharedInstance;

    调用sharedInstance会创建并返回单例

    调用purgeSharedInstance会销毁单例

    手动调用alloc也可以保证是单例,你可以这样调用

    [[MyClass alloc] initWithParam:firstParam secondParam:secondParam];

    只是要保证在sharedInstance之前调用,因为只有一次创建机会。

    下面是使用宏的写法“ 

    MyClass.h: 
    ======================================== 
        #import "SynthesizeSingleton.h" 
    
        @interface MyClass: SomeSuperclass 
        { 
            ... 
        } 
        SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(MyClass); 
    
        @end 
    ======================================== 
    
    
        MyClass.m: 
    ======================================== 
        #import "MyClass.h" 
    
        @implementation MyClass 
    
        SYNTHESIZE_SINGLETON_FOR_CLASS(MyClass); 
    
        ... 
    
        @end 
    ========================================

    开源库下载地址

    3、

    iOS在4.0以后推出了blockGCD,这两个特性给iOS开发带来的很大的便利,也使开发变得更加趣味话。那么如何通过GCD+block来实现单例模式呢,这主要归功于dispatch_once(dispatch_once_t *predicate, ^(void)block)这个GCD的函数,他有两个参数第一参数是一个指向dispatch_once_t类型结构体的指针,用来测试block是否执行完成,该指针所指向的结构体必须是全局的或者静态的,第二个参数是一个返回值与参数均为空的block,在block体中进行对象的初始化即可。dispatch_once在程序的生命周期中保证只会被调用一次,所以在多线程中也不会有问题。 该种方法使用方法:

    + (Singleton *)sharedInstance
    {
        static Singleton *instance = nil;
    
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[Singleton alloc]init];
        });
    
        return instance;
    }


    dispatch_once的作用就是执行且在整个程序的声明周期中,仅执行一次某一个block对象。简直就是为单例而生的嘛。而且,有些我们需要在程序开头初始化的动作,如果为了保证其,仅执行一次,也可以放到这个dispatch_once来执行。

    然后我们看到它需要一个断言来确定这个代码块是否执行,这个断言的指针要保存起来,相对于第一种方法而言,还需要多保存一个指针。

    方法简介中就说的很清楚了:对于在应用中创建一个初始化一个全局的数据对象(单例模式),这个函数很有用。

    如果同时在多线程中调用它,这个函数将等待同步等待,直至该block调用结束。

    这个断言的指针必须要全局化的保存,或者放在静态区内。使用存放在自动分配区域或者动态区域的断言,dispatch_once执行的结果是不可预知的。

    总结:1.这个方法可以在创建单例或者某些初始化动作时使用,以保证其唯一性。2.该方法是线程安全的,所以请放心大胆的在子线程中使用。(前提是你的dispatch_once_t onceToken

    对象必须是全局或者静态对象。这一点很重要,如果不能保证这一点,也就不能保证该方法只会被执行一次。)


    参考博客:

    水滴石穿 Keeping faith.      --- wtlucky's Blog

  • 相关阅读:
    公式在Excel报表中的应用
    Excel生成报表之解决方案设置单个单元格格式
    Excel生成报表之解决方案开盘盘高盘低收盘图(附源码)
    Excel生成报表之解决方案设置多个单元格区域的格式
    Excel生成报表之解决方案簇状圆锥图
    Excel生成报表之解决方案设置整行整列(附源码)
    已发布Acey.ExcelX3.4英文和中文版
    Excel生成报表之解决方案气泡图
    Excel生成报表之解决方案单元格富文本
    Excel生成报表之解决方案单元格添加批注
  • 原文地址:https://www.cnblogs.com/ymonke/p/3513668.html
Copyright © 2020-2023  润新知