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

什么单例

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

为什么用单例模式

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

创建一个单例条件

  • 提供一个静态实例,一般设置成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

    原文作者:EmptyWalker
    原文地址: https://www.jianshu.com/p/fbfeff2b19ef
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞