iOS 设计模式之单例模式

1,298 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情


设计模式相信大家都很熟悉了,但是越是基础的东西也越是需要经常的回顾与思考,于是着手写一个温故而知新的设计模式回顾系列。
在 8 月,准备把各个常用的不常用的设计模式都进行一次回顾,并大概聊一下各个模式的特点与在 iOS 中的大概使用场景与情况。

单例模式

1、认识单例模式

首先让我们先看下关于单例模式的定义(来自于《设计模式》(Addison-Wesley,1994))

一个类有且仅有一个实例,并且自行实例化向整个系统提供。

如果说每一个人都是一个类,那么从他出生开始,他就是生活中的唯一实例,每当有人要拜访或者联系你的时候,无论别人认识你的时候你是什么状态,他所能联系到的都是现在的你。你本身的状态会在其他地方发生改变,即当你状态改变后,后续所有找到你的,都是看到状态改变后的你。那么,我们就可以认为每一个人都处于单例模式。

在 iOS 中,系统默认就有许多支持单例方法的地方,例如通知中心、应用管理、本地保存等,但是一般都属于非严格的单例模式,也就是你可以使用 [UIApplication sharedApplication] 来获取单例对象,也可以通过 [UIApplication new] 生成一个新的对象,而如果按照准确的单例定义来说,依旧以 UIApplication 类为例, [UIApplication new][UIApplication sharedApplication] 应该在任何时候返回的对象都是相同的。也就是说无论你怎么操作,只会存在一个实例,不会创建其他的副本内容。

2、什么场景使用单例模式?

  • 当你需要在整个程序的运行周期中,让一个类始终保持同一个实例。则必须使用单例模式;(例如 [UIApplication sharedApplication]
  • 在生命周期中管理数据,例如自定义的管理中心或者[NSUserDefaults standardUserDefaults]
  • 为了节省内存,部分类可以使用单例模式,例如多处使用的图片等。

其实能够理解懒加载的话,相信理解单例模式

3、单例模式的使用

单例模式需要实现一个公共访问的类方法,一般命名为 shared + 类名(例如老演员 sharedApplication )。在该方法的具体实现方案,是推荐通过 dispatch_once 来实现类的实例化。(也就是第五点 demo 所展示的)

可以直接通过重写父类的方法,把分配内存的方法变成只执行一次。从根本上实现了单例。

如果按照严格的单例写法的话,单例模式一共需要重写五个方法:

+ (instancetype)allocWithZone:(struct _NSZone *)zone
+ (id)copyWithZone:(struct _NSZone *)zone
+ (id)mutableCopyWithZone:(struct _NSZone *)zone
- (id)copyWithZone:(NSZone *)zone
- (id)mutableCopyWithZone:(NSZone *)zone

以保证在任何情况下,都只会存在一种实例。(当然,也可以把这些方法包括 alloc、new、copy、mutableCopy 等方法通过 attribute((unavailable("invalid"))); 方式直接禁止,以保证一定使用 shared 的单例方法)

SingleView.h

#import <Foundation/Foundation.h>

@interface SingleView : NSObject

#pragma mark- method
+ (SingleView *)sharedSingleView;
+ (id)alloc __attribute__((unavailable("invalid, use sharedSingleView instead")));
+ (id)new __attribute__((unavailable("invalid, use sharedSingleView instead")));
- (id)copy __attribute__((unavailable("invalid, use sharedSingleView instead")));
- (id)mutableCopy __attribute__((unavailable("invalid, use sharedSingleView instead")));

@end

具体表现即为:

截屏2022-07-31 12.03.41.png

如果只是在自己的项目中使用的话,那么直接实现一个 shared 的类方法基本都能满足需求,由于是自己的内容,代码是受控的,实现并调用即可。而如果是对外的库的话(静态库、动态库),这需要根据具体的业务内容考虑是否需要按照严格的单例写法来实现了。

4、总结

单例模式是一种非常常见、使用频率也很高的一种设计模式。单例能够在许多场合使用,有时候也可以用来保存数据。

(严格单例)存在着以下特点:

  1. 节省内存;
  2. 在程序的运行周期中保存数据;
  3. 只能在自己的类中实现实例;
  4. 程序的运行周期中,内存不会被释放;
  5. 类中有且仅有一个实例;

5、demo

SingleView.h

#import <Foundation/Foundation.h>

@interface SingleView : NSObject

#pragma mark- method
+ (SingleView *)sharedSingleView;
@end

SingleView.m

#import "SingleView.h"

@implementation SingleView

#pragma mark- public method
+ (SingleView *)sharedSingleView {
    static SingleView *single = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        single = [[super allocWithZone:NULL] init];
    });
    return single;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [SingleView shareSingleView];
}

+ (id)copyWithZone:(struct _NSZone *)zone {
    return [SingleView shareSingleView];
}

+ (id)mutableCopyWithZone:(struct _NSZone *)zone {
    return [SingleView shareSingleView];
}

- (id)copyWithZone:(NSZone *)zone{
    return  [SingleView shareSingleView];
}

- (id)mutableCopyWithZone:(NSZone *)zone{
    return [SingleView shareSingleView];
}
@end