单例模式(一)

单例是在整个项目中,一个类只能创建一个实例,而且方便供外界访问。所以实现单例模式最主要的就是要实现一个类只能创建一个实例。

以SpiderMan类为例子

1、类在创建实例的过程中,都会通过alloc来获取内存空间,要想类只能创建一个实例就必须得在alloc的时候控制类只创建一个实例。每次alloc的时候都会调用+ (id)allocWithZone:(struct _NSZone *)zone方法,多以应该在该方法中对实例进行处理。

首先需要一个全局外部变量 _instance,至于为什么要使用static,后面再说明。

static id _instance;

在allocWithZone:方法中创建这个实例

+ (id)allocWithZone:(struct _NSZone *)zone
{   
     if (_instance == nil) { // 防止创建多次
           _instance = [super allocWithZone:zone];
     }
     return _instance;
}

这样就基本上实现了一个类就只创建一个实例的功能。但是这并不完善,因为在多线程中,该类有可能创建不同的实例。所以为了保证该类只能创建一个实例还必须得考虑线程问题。所以为了解决该问题必须得给创建实例的代码加上一个线程锁,如:

+ (id)allocWithZone:(struct _NSZone *)zone
{
    @synchronized(self) {
        if (_instance == nil) { // 防止创建多次
            _instance = [super allocWithZone:zone];
        }
    }
    return _instance;
}

这样在线程上保证了单例性,但有点不足的是,每次创建实例的时候,都会重复地加锁,所以再做如下处理:

+ (id)allocWithZone:(struct _NSZone *)zone
{
    if (_instance == nil) { // 防止频繁加锁
        @synchronized(self) {
            if (_instance == nil) { // 防止创建多次
                _instance = [super allocWithZone:zone];
            }
        }
    }
    return _instance;
}

2、一般地为了方便创建实例都会提供一个类方法shareXXX,而一般不会通过alloc来创建实例,所以给SpiderMan添加一个类方法

+ (id)shanreSpiderMan
{
    if (_instance == nil) { // 防止频繁加锁
        @synchronized(self) {
            if (_instance == nil) { // 防止创建多次
                _instance = [[self alloc] init];
            }
        }
    }
    return _instance;
}

3、还需要考虑的一个问题就是:如果单实例通过copy来拷贝一个对象时,这时拷贝的对象是一个新的对象,SpiderMan有了两个不同的对象,这与初衷相悖,所以为了解决这个问题

- (id)copyWithZone:(NSZone *)zone
{
    return _instance;
}

此时,单例基本上已经实现了,使用时只需要通过调用类方法shareSpiderMan就可以获得单例对象了。

现在再来看看前面提到的一个问题:为什么使用static?首先这个static可以用来修饰函数,也可以用来修饰变量,在这里修饰的是变量。另外还需要知道的一点就是,全局变量是可以在整个项目当中使用的,比如在该SpiderMan类中的_instance全局变量,如果没有加static的话,它可以在别的类中被访问到,通过extern id _instance即可以访问得到并修改该_instance。而static修饰的全局变量是仅限于当前文件内部的,外部文件是访问不了的即使是使用extern也不能访问到。

OK,这样就实现了单例模式了,详细代码已上传到Github:https://github.com/Anchlate/Singleton-SpiderMan。

完善部分请看下一篇单例模式(二)

你可能感兴趣的:(单例模式(一))