系列:iOS开发-单例模式

系列:iOS开发-单例模式

在我们做开发的时候经常会遇到需要在很多地方调用同一个对象的现象,
比如在一个类中,我们可能会用到全局变量,在一个方法中会重复用到某个局部变量...

今天要说的单例模式就是一个类似这样的存在

  1. 单例模式的作用 :可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问,从而方便地控制了实例个数,并节约系统资源.
  2. 单例模式的使用场合:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次),应该让这个类创建出来的对象永远只有一个. 即一旦你创建了一个单例类,不论你在多少个界面中初始化调用了这个单例方法取得对象,它们所有的对象都是指向的同一块内存存储空间(即单例类保证了该类的实力对象是唯一存在的一个).

在iOS中开发中,单例模式是最常使用的设计模式之一。单例模式不需要传递任何参数,就有效地解决了不同代码间的数据共享问题。单例类是一个非常重要的概念,因为它们表现出了一种十分有用的设计模式。单例类的应用贯穿于整个iOS的SDK中。例如,UIApplication类有一个方法叫sharedApplication,从任何地方调用这个方法,都将返回与当前正在运行的应用程序相关联的UIApplication实例。除了这个,NSNotificationCenter(消息中心) 、NSFileManager(文件管理) 、 NSUserDefaults(持久化存储数据) 、NSURLCache(请求缓存)、NSHTTPCookieStorage(应用程序cookies池)都是系统单例;

总而言之,单例类保证了应用程序的生命周期中有且仅有一个该类的实例对象,而且易于外界访问。

那么我们来看看他的设计思路应该是什么?

  1. 永远只分配一块内存来创建对象
  2. 提供一个类方法,返回内部唯一的一个变量
  3. 最好保证init方法也只初始化一次
    因为只有这样才能保证无论在任何时候创建这个类的对象,都应该是固定的一个

OK,我们既然有思路了,我们应该怎么创建呢?
我们先看看一个普通的类的init方法

- (instancetype)init{
    self = [super init];
    return self;
}

但是这个是实例方法,我们需要改成类方法,所以我们仿照着创建一个方法

static DemoSingleton *_instance = nil;
+(DemoSingleton *)shareDemoSingleton{
    if (_instance == nil) {
        _instance = [[self alloc]init];
    }
    return _instance;
}

首先定义一个全局变量,接着在创建方法中做判断,如果有就直接返回,没有就创建之后返回,那这个就是一个有效的单例的创建模式.
我们打印看看


这里写图片描述

我们发现两次创建的对象的地址都是同一个,那代表什么意思呢?是的,这就是一个单例对象,我们可以在任何地方调用这个类方法来得到这个对象,它活跃于整个应用的周期.

但是学过并发的我们可能会想到一点,如果开始没有,然后有两个地方同时创建,会怎么样呢?是的,这样就会导致创建两个不一样的对象,那么久还打不到我们要的效果,
于是有了新的方法,我们加一个锁

+(DemoSingleton *)shareDemoSingleton{
    NSLock *lock = [[NSLock alloc]init];
    [lock lock];
    if (_instance == nil) {
        _instance = [[self alloc]init];
    }
    [lock unlock];
    return _instance;
}

或者

static DemoSingleton *instance = nil;
+(DemoSingleton *)shareDemoSingleton{
    @synchronized (self) {
        if (_instance == nil) {
            _instance = [[self alloc]init];
        }
        return _instance;
    }
}

当然我们后期也会学到iOS中的GCD,我们也可以使用这中方法

static DemoSingleton *_instance = nil;
+(DemoSingleton *)shareDemoSingleton{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc]init];
    });
    return _instance;
}

大家都可以尝试看看,效果都是一样的,主要的目的只有一个,加锁,防止同时创建,
当然对于每个类,iOS本身都提供一个类方法创建+(instancetype)allocWithZone:(struct _NSZone *)zone

所以我们为了严谨,也需要在这个地方写上同样的代码

+(instancetype)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}

不仅仅如此,我们还有copyWithZone 和 mutableCopyWithZone
我们需要修改下

-(id)copyWithZone:(NSZone *)zone
{
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
    return _instance;
}
这里写图片描述

我们发现无论是创建,还是拷贝等,永远都只有一个对象了,
那么这个就是我们要的单例效果了,

但是我们是不是每一次创建单例都需要这么复杂呢?
我们可以写一个宏,

// @interface
#define singleton_interface(className) \
+ (instancetype)shared##className;


// @implementation
#define singleton_implementation(className) \
static className *_instance; \
+ (instancetype)shared##className \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
+ (instancetype)allocWithZone:(NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
- (instancetype)copyWithZone:(NSZone *)zone{ \
return _instance; \
} \
- (instancetype)mutableCopyWithZone:(NSZone *)zone{ \
return _instance; \
}

看上去有点复杂?


这里写图片描述

这里写图片描述

这里写图片描述

OK 引入.h文件,只要写两行,所有的就搞定了.
我们同样运行看看


这里写图片描述

是不是一样的结果?

OK ,以上是单例的写法,
那么单例的作用是什么呢?
简单举个例子,比如我开发的是一个音乐播放软件,那么我会想到的是我会创建一个音乐播放器的类 它有几个方法,比如播放,暂停,快进,上下曲等...
另外我还得保证我整个应用就只有一个这样的播放器,所以我在整个应用任何地方调用播放器的方法的时候都需要保证我操作的是那唯一的播放器,否则就会出现问题.那么此时,我就很适合使用单例模式,这个播放器类写成一个单例,这样无论是在什么地方创建,获取或着拷贝等,永远都是那个正在使用的播放器...

当然还有,比如我有很多信息数据是需要在整个应用都存在,也是唯一的,比如用户信息,我也可以写成单例,方便任何地方调用,(当然也可以写到数据库或者沙盒中,或者其他方式...)

iOS在开发中,给我们提供了几个自带的单例,不要认为它没有什么作用,比如NSUserDefaults,我们做持久化就要保证的唯一要求就是写在一个地方,读取那肯定还要在那个地方,否则就读取不到了,之所以写成了单例,本身的要求其实就是唯一性.

OK,单例只是一种设计模式,本身并不复杂,他还是一个类,任何处理都是一个普通的类,只是所有的方法操作的对象是唯一而已.

Demo地址:https://github.com/spicyShrimp/DEMO_OC

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容