iOSSingleton设计模式详细的说明教程

        iOS有很多的设计模式,当然,不管是什么语言有很多的设计模式。辛格尔顿是一种之一,辛格尔顿,它从字面上是一个单独的实例,首先,它是只有一个单一的,其次,它是一个实例。我们知道,在iOS用于开发Objective—C语言是面向对象的语言,我们说的实例通常就是指我们创建的对象。而用来创建单例(唯一实例)的类就是单例类,这一点不难理解。

        单例:

不管一个应用程序请求多少次,单例类都始终返回的是同一个实例对象。

一个典型的类在用户须要的情况下会创建非常多个对象。而一个单例类在一个应用程序其中仅仅会创建唯一的一个实例。一个单例对象提供一个资源点供全局訪问。单例通常在单点控制值得使用的时候使用,比方一个类要提供一些通用的服务和资源。

        单例类:

        我们通常通过一个工厂方法从单例类中获取一个全局的实例变量。

当第一次被请求的时候。这个类会懒载入它的唯一实例(单例),以此确保此后没有其他实例被创建。一个单例类同一时候也阻止用户copy、retain或者release这个实例。

假设我们须要。我们能够创建我们自己的单例类(系统有非常多单例类,以下补充说明)。比方我们须要一个类为其他应用程序中的对象提供声音的时候。我们能够创建一个单例。

       补充:

       有几个Cocoa框架的类是单例类,包含NSFileManager、NSWorkSpace,而且在UIKit其中,UIApplication,UIAccelerometer都是单例类。依照惯例,工厂方法返回单一实例的名称格式,形式是shareClassType(share + 类型名)。就拿Cocoa框架举例来说,工厂方法都称之为“sharedFileManager、sharedColorPanel和sharedWorkspace”。

       图示:

       左图表示普通类创建对象,当外界须要的时候创建一次对象。可是每次创建的对象都不一样。

右图表示单例类创建的对象。不管外界请求多少,返回的对象都是唯一的,这样在实际开发其中,我们就能够通过单例来给外界提供共同拥有的资源和服务来使用,比方多个页面须要同一个数据。就能够在多个页面訪问唯一的单例。

       

       准备:

       在学习单例之前。除了OC主要的语法之外,你须要这些知识储备:

       一、必备知识

       1、创建一个对象的基本形式。

       2、内存管理的基本知识。

       3、工厂方法的了解。

      二、相关知识:

       1、对象的拷贝

       2、内存管理高级。

       3、多线程知识。

       假设你在这些方面有一些欠缺,可能不会直接导致你理解不了单例设计模式,可是会添加难度,所以我还是希望我们有一定的学习基础再去涉及设计模式的知识。当然大家全然能够通过搜索或者查阅资料做一些先决的知识储备。


        单例的基本知识我们已经介绍完了,如今我们開始去学习怎样创建一个单例。我们先来创建一个最简单的单例,也就是“伪单例”,是我们经常使用的方式。然后再一步一步健全它。

        第一步:我们创建一个空project(EmptyApplication),而且在project中搜索,gar,启用MRC(方便我们研究和学习一个完整的单例)。

             

       

        第二步:我们创建一个单例类(WDSingletonManager。当然名字自己定义。你能够起名为“A”,仅仅要你开心,别忘了继承自NSObject),而且分别在.h、.m中书写下面代码。

        接口部分(.h中)代码:

        

#import <Foundation/Foundation.h>

@interface WDSingletonManager : NSObject

//去掉前缀。share + 类名
+ (instancetype)shareSingletonManager;

//当然你的类名假设是 XX + DataHandle(XX,代表开发人员自己定义的前缀,通常是你称呼的首字母组合或者团队的缩写)。那么你的方法名依照苹果推荐的命名规范应该例如以下书写(仅仅是建议,对程序本身无影响):
//+ (instancetype)shareDataHandle;


@end

        实现部分(.m中)代码:

         

@implementation WDSingletonManager

//实现创建单例的工厂方法
+ (instancetype)shareSingletonManager{

    //声明一个静态指针变量用于保持指针指向的对象的唯一。static这一行代码在整个project中仅仅运行一次。
    static WDSingletonManager *singletonManager = nil;
    //懒创建方式,假设没有才创建,有的话直接返回,if的分支语句仅仅有外界第一次调用的时候才运行。
    if (nil == singletonManager) {
        singletonManager = [[self alloc]init];
    }
    return singletonManager;
}


@end

          好了,至此,一个伪单例类就创建完毕了,这样我们每次调用的时候。看一下对象是否唯一呢?一起来验证一下。

         


        验证发现。结果是我们想要的。也就是说我们不管创建多少个对象,实际上都是同一个实例(由于始终在堆区始终是同一块内存空间)。当然,在实际开发中,你也能够给这个单例加一个属性或者方法。达到每一个页面或者每一个页面的每一个地方都能够訪问或者改动的目的。这就是我们主要的一个单例。当然这也是不完整的。

不完整在哪里。我们一起来思考一下。

通常创建一个对象都是能够使用动静态方法结合来创建的,比方id obj = [[NSObject alloc]init];//或者 id _obj = [NSObject new];,那么我们在使用单例的时候没有谁规定一定使用我们shareClassType类似的方法去创建,所以为了保证对象的唯一性。我们须要在使用alloc + init的方法的时候也须要保证创建的对象唯一性。

那怎么办到呢?事实上我们每次在调用alloc方法的时候,系统都会去调用一个allocWithZone的方法。所以我们须要重写此方法。那么单例是唯一的,我们在程序中多个页面都须要使用,那么当中的一个页面或者一个地方万一release掉呢?retain呢?这时候我们的单例都无法保证它的特性。

所以,我们要把内存管理的问题综合考虑进去。我们须要完好一下。

第三步:完好步骤(在我们的.m中书写)。

        注:在静态方法(类方法)中,self代表的的是类,在动态方法中(对象方法。也就是俗称的“减号方法”)。self代表的是当前的对象,由于单例类的唯一性,所以我们在调用静态方法的时候。返回self和返回我们自己的静态变量实质是一样的。

        

#import "WDSingletonManager.h"
@implementation WDSingletonManager
//声明一个静态指针变量用于保持指针指向的对象的唯一,static这一行代码在整个project中仅仅运行一次。

static WDSingletonManager *singletonManager = nil; //实现创建单例的工厂方法 + (instancetype)shareSingletonManager{ //懒创建方式,假设没有才创建,有的话直接返回,if的分支语句仅仅有外界第一次调用的时候才运行。 if (nil == singletonManager) { singletonManager = [[self alloc]init]; } return singletonManager; } + (instancetype)allocWithZone:(struct _NSZone *)zone{ if (!singletonManager) { //记住这里不是self,而是super,由于self调用alloc或者allocWithZone都会导致递归。用super能够有效的避免 singletonManager = [super allocWithZone:zone]; } return singletonManager; } //重写init方法,保证初始化返回的对象唯一。

- (instancetype)init{ if (!self) { self = [super init]; } return self; } //直接返回自己,不让内部做不论什么操作,确保对象唯一 - (instancetype)retain{ return self; } //直接返回自己。不让内部做不论什么操作,确保对象唯一 - (id)copy{ return self; } //直接返回自己,不让内部做不论什么操作,确保对象唯一。只是须要先遵守NSCopying协议 - (id)copyWithZone:(NSZone *)zone{ return self; } //不让释放。重写方法。不做不论什么操作就可以。(one way代表此操作无法“回滚”。即不能撤销) - (oneway void)release{ } //差别于release,autorelease是有返回值的,我们仅仅须要返回自己(self)或者返回我们的单例(singletonManager)就能够 - (instancetype)autorelease{ return self; } //下面方法可不写,当然包含dealloc方法也不须要重写 //不让外界訪问我们的引用计数(模仿系统的类簇比方NSString的retainCount),给出一个不可能的值(无实际意义。装逼用) - (NSUInteger)retainCount{ return NSIntegerMax;//或者-1,也能够。 } @end

       补充:非常多人告诉大家,关于整个单例类的设计中,init的相关方法能够不写,由于alloc已经分配了空间,这是错误的结论。我希望大家对此有一个认知。

alloc,是静态的空间分配,init是动态的初始化过程。普通情况下他们返回的地址是一样的,可是也会有例外。

所以我们无法保证。以下这段代码(大家复制到自己的project去验证)就告诉大家不要轻易的相信alloc和init返回的对象地址是一致的,当然这仅仅是个例,只是我还是希望大家注意。

       
    NSMutableArray *array = [NSMutableArray alloc];
    NSLog(@"%p",array);
    array = [array init];
    NSLog(@"%p",array);

       较完好的单例创建就已经OK了,这是我们眼下在MRC情况下考虑比較全面的情况,也是按照苹果官方的建议书写的单例。可是随着我们学习知识的全面性。依旧有一些问题暴露出来了,比方多线程,那么。多线程会出现什么问题呢?当两个线程同一时候訪问一个单例,这时候假设单例为nil,那么他们同一时候去创建单例,又两个线程同一时候创建单例对象,显然又不符合我们的要求。

那么我们须要怎么做呢?我们须要进行多线程情况下的保护操作。仅仅须要给以下这三个方法加锁就能够了。    


第四步:多线程问题

     

+ (instancetype)shareSingletonManager{
    
    //确保每次仅仅有一个线程能够訪问
    @synchronized(self){
        
        if (nil == singletonManager) {
            singletonManager = [[WDSingletonManager alloc]init];
        }
        
    }
    
    return singletonManager;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    
    @synchronized(self){
        
        if (nil == singletonManager) {
            singletonManager = [super allocWithZone:zone];
        }
        
    }
    return singletonManager;
}
- (instancetype)init{
    
    @synchronized(self){
        
        if (!self) {
        self = [super init];
        }
    }
    return self;
}

        当然,这还不是唯一的,由于我们知道多线程也能够创建单例。也省去了我们考虑多线程在内的因素。就是用以下的方法去创建单例(替换掉第一个加号方法就能够),能够有效的避免,至于涉及到的多线程知识,那就是dispatch_once了,我们知道dispatch_once是在整个程序的生命周期中仅仅执行一次,所以有效的避免了各种问题。包含多线程訪问的问题。       

+ (instancetype)shareSingletonManager{

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singletonManager = [[self alloc]init];
    });
    return singletonManager;
}

         好了,我知道赘述到这里,相信大家已经对单例有了一个具体的了解了。希望能够帮到大家。当然,实际开发中我们可能仅仅须要一个伪单例,或者是在ARC的情况下去处理一些问题。有时候不会碰到这么多的问题。所以我们仅仅是做一个具体的解释,供大家參考。不要一味的追寻完整性,使代码冗余,高效的代码才是我们追求的。当然。单例是一种非常好的设计模式。做解析数据的公共数据源。界面传值等等都是非常好的方案。

只是也不要乱用。由于一旦创建了单例,仅仅有程序被kill掉单例才会消失,所以对于内存精确控制的学生,一定要不要滥用单例。

        有疑问欢迎随时沟通,我的新浪微博:己怽。

        我也会更新我的视频提供给大家,欢迎有须要的朋友互相交流学习。




也许更新:http://v.youku.com/v_show/id_XMTMyMTA4NjM5Mg==.html(删除原始视频。新记录,的一些内容更新和完整性,请选择至少高清播放)

        注意:因此上述代码和演示。被Xcode6.3.2版本号验证。


原文地址:https://www.cnblogs.com/lcchuguo/p/5027518.html