我所理解的设计模式 —— 单例

什么单例

单例是指整个程序中有且只有一个对象,具有全局唯一性,单例必须要自行创建一个实例,单例必须要始终提供一个全局实例暴露给外部使用。

为什么用单例模式

创建并使用一个单例,就是引入了单例模式;使用单例模式,是为了避免在整个程序中创建多个对象。

创建一个单例条件

  • 提供一个静态实例,一般设置成nil
  • 提供一个创建单例实例的方法,如果实例存在就返回实例,否则创建一个实例
  • 在OC中,需要重写+ (instancetype)allocWithZone:(struct _NSZone *)zone方法,因为这个方法会在allocnew等方法调用后调用

allocWithZone:(struct _NSZone *)zonealloc

实际上alloc内部会调用allocWithZone,也就是说allocWithZone方法更底层。也就是说我们实现alloc方法的话就只能拦截alloc方法,但是实现allocWithZone方法的话,任何内存分配的方法我们都能拦截。
NSZone是用来分配和管理一段内存空间的结构体,查找NSZone的头文件能够发现以下函数

FOUNDATION_EXPORT NSZone *NSCreateZone(NSUInteger startSize, NSUInteger granularity, BOOL canFree) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");

FOUNDATION_EXPORT NSString *NSZoneName(NSZone * __nullable zone) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT NSZone * __nullable NSZoneFromPointer(void *ptr) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");

FOUNDATION_EXPORT void *NSZoneMalloc(NSZone * __nullable zone, NSUInteger size) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT void *NSZoneCalloc(NSZone * __nullable zone, NSUInteger numElems, NSUInteger byteSize) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT void *NSZoneRealloc(NSZone * __nullable zone, void * __nullable ptr, NSUInteger size) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");
FOUNDATION_EXPORT void NSZoneFree(NSZone * __nullable zone, void *ptr) NS_SWIFT_UNAVAILABLE("Zone-based memory management is unavailable");

看起来跟C语言的malloc,calloc,free等函数相似,所以大概NSZone就是可以分配一段连续空间。你可以将你创建的类实例都存在这个空间,自己管理。

比如你都用allocWithZone创建三个NSDictionary,那这三个字典肯定是连在一起存储的。然后你连着读取三字典的数据,地址相邻理论上读取速度更快。

反正当年肯定用这个有好处,现在苹果都帮你处理好了,不用管了,官方也不建议再用这个接口了。

总结下来就是:
allocallocWithZone都是创建一个类实例的方法
不同之处在于,alloc无法指定一个NSZone来存储自己创建的实例,它最终调用的是allocWithZone(nil),使用的是系统给定的NSZoneallocWithZone可以指定自己的NSZone来存储自己创建的实例,如果zonenil.它使用的就是系统给定的NSZone

单例的实现

1. 非线程安全

.h文件,下面的几种方法该文件都是提供相同的方法,不在添加

#import <Foundation/Foundation.h>

@interface XYHSingleton01 : NSObject

+ (instancetype)shareInstance;

@end

.m文件

#import "XYHSingleton01.h"

@implementation XYHSingleton01
//标准的单利模式 (确定没有多线程的情况   可以使用)
static XYHSingleton01 *_instance = nil;

+ (instancetype)shareInstance {
    if (_instance == nil) {
        _instance = [[XYHSingleton01 alloc] init];
    }
    return _instance;
}
//当我调用alloc的时候 回调改方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    if (_instance == nil) {
        _instance = [super allocWithZone:zone];
    }
    return _instance;
}
@end

之所以说是非线程安全,是因为,若在多线的情况下,假设线程A和线程B中都在获取这个_instance,此时,A执行_instance = [[XYHSingleton01 alloc] init];方法时,还没有执行结束的时候,B又调用shareInstance方法,但是此时_instance还没有创建成功,所以B又会去alloc一个新的对象,从而不满足单例模式。
这种方法,可以应用在确保没有多线程调用的情况下使用,可以提升程序性能,因为加线程保护是性能消耗的。

2. 线程安全(dispatch_once)

.m文件

#import "XYHSingleton_02.h"

@implementation XYHSingleton_02

static XYHSingleton_02 *_instance = nil;

+ (instancetype)shareInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[XYHSingleton_02 alloc] init];
    });
    return _instance;
}

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

dispatch_once函数式保证在应用程序执行中只执行一次指令处理的API,通过dispatch_once函数,能够保证实例即使在多线程的情况下,也只初始化一次。

3. @synchronized关键字

.m文件

#import "XYHSingleton_04.h"

@implementation XYHSingleton_04

static XYHSingleton_04 *_instance = nil;

+ (instancetype)shareInstance {
    @synchronized (self) {
        _instance = [[XYHSingleton_04 alloc]init];
    }
    return _instance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    if (!_instance) {
        @synchronized (self) {
            _instance = [super allocWithZone:zone];
        }
    }
    return _instance;
}
@end

@synchronized 结构所做的事情跟锁(lock)类似:它防止不同的线程同时执行同一段代码。但在某些情况下,相比于使用 NSLock 创建锁对象、加锁和解锁来说,@synchronized 用着更方便,可读性更高。

4. 使用load方法

.m文件

#import "XYHSingleton_03.h"

@implementation XYHSingleton_03
static XYHSingleton_03 *_instance = nil;

+ (void)load {
    [XYHSingleton_03 shareInstance];
}

+ (instancetype)shareInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[XYHSingleton_03 alloc] init];
    });
    return _instance;
}

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

+ load 作为 Objective-C 中的一个方法,与其它方法有很大的不同。它只是一个在整个文件被加载到运行时,在 main 函数调用之前被 ObjC 运行时调用的钩子方法。所以将实例的初始化放在load方法中,即可在程序启动后就创建了一个_instance,这个之前懒加载模式(什么时候用什么时候去创建)有点区别。

引用

allocWithZone
@synchronize
+ load

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

推荐阅读更多精彩内容