iOS单例安全写法

单例模式很常见,但是,能真正把单利模式写对的却很少。在iOS中,一般我们都是用官方推荐的写法来写单例:

#import "ZXYHelper.h"

@implementation ZXYHelper

+ (instancetype)sharedInstance
{
    static ZXYHelper *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[super allocWithZone:NULL] init];
    });
    return instance;
}

@end

这也是我们单例模式的标准写法。 在使用时,我们只需要

ZXYHelper* p= [ZXYHelper sharedInstance];

就可以用 p 这个单例来干很多事情了,看起来没有问题。在很多实际的项目中,很多人也的确是这么做的。

问题:
可是,在多人开发中,并不能保证所有人都会使用 sharedInstance 方法来创建对象;而一旦有人用 alloc,new 等来创建对象,这就不是单例了。例如:

    ZXYHelper * p1 = [ZXYHelper sharedInstance];
    NSLog(@"单利:p1:%@",p1);

    ZXYHelper * p2 = [[ZXYHelper alloc] init];
    NSLog(@"单利:p2-alloc:%@",p2);

    ZXYHelper * p3 = [ZXYHelper new];
    NSLog(@"单利:p3-new:%@",p3);

可以看到,p1、p2、p3不是同一个对象,而所谓单例,就是不管我用何种方法创建对象,都必须是同一个。所以,单例模式,绝不是一个 sharedInstance 就够了。

单利:p1:<ZXYHelper: 0x600002a80590>
单利:p2-alloc:<ZXYHelper: 0x600002a84590>
单利:p3-new:<ZXYHelper: 0x600002a97720>

方案一

那么如何避免这种问题呢?我们知道:在对象创建的时候,alloc、new都会调用到 allocWithZone: 方法;使用拷贝创建对象时,会调用 copyWithZone: 、mutableCopyWithZone:方法;那么,重写这些方法,就可以让创建的对象唯一。

#import "ZXYHelper.h"

@implementation ZXYHelper

+ (instancetype)sharedInstance
{
    static ZXYHelper *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[super allocWithZone:NULL] init];
    });
    return instance;
}

+ (id)allocWithZone:(NSZone *)zone
{
    return [[self class] sharedInstance];
}

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

- (id)mutableCopyWithZone:(NSZone *)zone
{
    return self;
}

@end

再看一下

单利:p1:<ZXYHelper: 0x600003788ad0>
单利:p2-alloc:<ZXYHelper: 0x600003788ad0>
单利:p3-new:<ZXYHelper: 0x600003788ad0>

都是同一个对象!大功告成!

方案二、

此外,还有一种方法,就是直接禁用掉 alloc、new 、copy等方法:
ZXYHelper.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ZXYHelper : NSObject

+ (instancetype)sharedInstance;
+(instancetype) alloc __attribute__((unavailable("replace with 'sharedInstance'")));
+(instancetype) new __attribute__((unavailable("replace with 'sharedInstance'")));
-(instancetype) copy __attribute__((unavailable("replace with 'sharedInstance'")));
-(instancetype) mutableCopy __attribute__((unavailable("replace with 'sharedInstance'")));
@end

NS_ASSUME_NONNULL_END

如此,在调用这些方法时就会报错,提示使用 sharedInstance 方法:

以此,也可以达到单例模式的要求,始终只有一个对象。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容