zoukankan      html  css  js  c++  java
  • 单例模式 (懒汉式)ARC

    为什么要使用单例模式:相比于代理更方便在不同类之间实现数据的传递,要点:保证某个类只有一个实例对象
    1
    static id _instance = nil;//定义一个静态全局变量 2 + (instancetype)allocWithZone:(struct _NSZone *)zone{ 3 4 if (!_instance ) {//如果存在直接返回,避免加锁过程 5 6 @synchronized(self) {//防止多线程抢夺资源, 7 8 if (_instance == nil) {//只有第一次才能进入里面创建, 9 10 _instance = [super allocWithZone:zone];//这是父类的, 11 } 12 } 13 } 16 return _instance; 18 }
    //copy方法可能会产生新对象,所以需要重写
    19 -(instancetype)copyWithZone:(NSZone *)zone{ 20 21 //这里不需要判断_instance 是否存在,copy是对象方法,来到这里一定存在 22 return _instance ; 23 24 }
    //要像外界提供单例获取方法
    25 +(instancetype)shareInstance{ 26 27 if (!_instance ) {//如果存在直接返回,避免加锁过程 28 29 @synchronized(self) {//保证同一时刻只有一个线程操作, 30 31 if (_instance == nil) {//保证只有一个对象.
    33 _instance = [[self alloc] init];//保证_instance不为空,init调用一次 35 } 36 } 37 return _instance ; 38 } 40 }

    实现关键点:allocWithZone这个方法,因为allocInit和New这两个个方法内部要调用alloc方法,alloc内部会调用allocWithZone.主旨是不要让当前类调用父类的allocWithZone这个方法,如果一旦调用了父类的方法,就会分配内存,我们实现单例的思想是:如果对象不存在,调用一次父类的方法allocWithZone,分配内存空间,并且把这个对像赋值给我们的自定义的全局变量static id _ instance,instance指向创建的这块内存空间,这样这个类就有了第一个对象,如果再次调用创建对象的方法,判断instance否存在存在,如果训在直接返回instance,不存在就创建一个.当然除了第一次都是不可能创建的.加锁是为了防止多线程抢夺资源,外层判断是防止频繁加锁耗性能,然后才成为我们看到的样子.

    下面说一下具体步骤:

    id _instance = nil;
    + (instancetype)allocWithZone:(struct _NSZone *)zone{ if (_instance == nil) { _instance = [super allocWithZone:zone];//这是父类的, } } return _instance ; }

    首先我们写成这样,当程序需要实例化一个对象的时候回调用alloc方法,alloc方法内部会执行allocWithZone这个方法,所以重写这个方法更合理.第一次进入这个方法时会判断对象是否存在,不存在创建一个对象,并将该对象赋值给我们定义的全局变量_instance,下次再次需要实例化对象时进入if判断,直接返回已经存在的对象,保证了程序当中只有一个实例对象.

    但是这样写还不完整,多线程的时候可能会在同一时间都通过if判断,创建多个对象或是带来意外的结果.将两个线程比喻成两个人盲人,同一时间两个人都在桌子上摸索,没有发现纸张(没有实例对象),然后每个人都各自在桌子上放一张纸,此时桌子上就有两张纸(多个实例对象),这是创建实例对象的过程可能会遇到的问题.取值的情况,两人同时在一张纸上写字(治好了),等到下次拿到的时候就蒙了,我只写了10字,拿到的确是20个字的,会造成数据的不准确.只是比喻一下多线程的情况.

    所以需要对线程加锁,同一时刻只有一个人对变量进行修改.所以写成下面的样子.同一时刻只有一个线程拿到变量操作

    + (instancetype)allocWithZone:(struct _NSZone *)zone{ 
        @synchronized(self) {//防止多线程抢夺资源,   
            if (_instance   == nil) {//只有第一次才能进入里面创建,       
                _instance   = [super allocWithZone:zone];//这是父类的,
            }
        } 
        return _instance    ;
    
    }

    但是单例可能会调用多次,每次都是加锁后再进行判断,很耗性能的,(加锁后面会有文章),如果我们的对象存在就直接返回实例对象就好了,就不用加锁之后在判断了.所以就写成下面的样子,这样写的目的就是防止频繁加锁,性能不好.

    + (instancetype)allocWithZone:(struct _NSZone *)zone{   
        if (!_instance  ) {//如果存在直接返回,避免加锁过程        
            @synchronized(self) {//防止多线程抢夺资源,           
                if (_instance   == nil) {//只有第一次才能进入里面创建,               
                    _instance   = [super allocWithZone:zone];//这是父类的,
                }
            }
        }  
        return _instance    ;   
    }

    但是此时还是有问题的,因为我们的全局变量在外部是可以被extern拿到修改的,我们在纸上写了好多字,突然被人拿到赋值为nil了,再次判断的时候instance就不存在了,就需要重新创建,原来纸张上面的信息就都没有了.也不能保证只有一张纸.所以需要用静态的全局变量,static id _instance.此时的全局变量只能在此文件的.m中使用,其他地方是拿不到的.所以就写成了下面的样子.使用id _instance 是这段代码有扩展性,其他类中需要单例时复制过去就可以了,如果写成某个具体的类 Person _instance,当Student类需要这段代码的时候需要修改.

    static id _instance = nil;
    + (instancetype)allocWithZone:(struct _NSZone *)zone{   
        if (!_instance  ) {//如果存在直接返回,避免加锁过程        
            @synchronized(self) {//防止多线程抢夺资源,           
                if (_instance   == nil) {//只有第一次才能进入里面创建,               
                    _instance   = [super allocWithZone:zone];//这是父类的,
                }
            }
        }  
        return _instance    ;   
    }

    此时ARC下的懒汉式单例我们已经完成,懒汉式:用到的时候再创建,与懒加载思想相同,后面会写饿汉式.但是出于良好的代码规范单例一般创建方法为Share,所以约定俗成的规范我们要向外界提供Share方法来获取单例.所以就有了Share的重写.写的严谨一些就哟啊考虑到各种使用情况,如果是NSString的对象,深拷贝Copy后会产生新对象,所以需要重写Copy方法,Copy是对象方法,我们直接返回instance就好,对象方法意味着对象来调用,因为我们已经做了单例,说明调用Copy的对象就是我们程序中唯一的单例对象,就不需要各种判断了,直接返回就可以了!

  • 相关阅读:
    设计模式 设计原则 何为设计
    面向对象 多态
    两个简易的对拍程序
    各类有用的神奇网站
    乘法逆元
    树链剖分
    Markdown的用法
    vimrc 的配置
    luogu【P1144】最短路计数
    【娱乐】收录各种神奇知乎问答
  • 原文地址:https://www.cnblogs.com/LDSmallCat/p/4954984.html
Copyright © 2011-2022 走看看