浅析单例设计模式

作者: VV木公子 | 来源:发表于2017-01-06 14:09 被阅读168次

    在开发中经常会用到单例设计模式,目的就是为了在程序的整个生命周期内,只会创建一个类的实例对象,而且只要程序不被杀死,该实例对象就不会被释放。保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    创建单例的几种方式

    方式一

    传统且正规的方法是把一次性代码dispatch_once写到allocWithZone:方法中去,目的是保证allocWithZone:方法只被调用一次,这样一来,凡是通过[[class alloc] init]方法创建的实例,都是指向同一个对象。代码如下:

    + (instancetype)shareInstance
    {
       ALYNetworkManager *instance = [[self alloc] init];
       return instance;
    }
    
    static ALYNetworkManager *_instance = nil;
    + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
       static dispatch_once_t onceToken;
       dispatch_once(&onceToken, ^{
           _instance = [super allocWithZone:zone];
       });
       return _instance;
    }
    
    -(id)copyWithZone:(NSZone *)zone
    {
       // 需要遵守NSCopying协议
       return _instance;
    }
    

    方式二

    更加简单的方式是把一次性代码dispatch_once写到获取单例对象的类方法中去。保证只要通过这个类方法获取的对象都是同一个实例。但是因为没有重写allocWithZone:方法,所以如果有开发者在外部直接调用[[class alloc] init]方法获取实例,那么获取的实例和通过类方法获取的实例就不是同一个实例。这样就不能保证实例的全局唯一性。所以,尤其在多人开发中,采用这种方式存在一定的风险性。代码如下:

    + (ALYNetworkManager *)sharedInstance {
        static ALYNetworkManager *instance;
        static dispatch_once_t token;
        dispatch_once(&token, ^{
            instance = [[ALYNetworkManager alloc]init];
        });
        return instance;
    }
    

    如下图,可以看到,通过shareInstance类方法获取的实例始终是同一个,而通过[[class alloc] init] 和 shareInstance类方法获取的实例并不是同一个:


    方式三

    另外,除了使用一次性代码dispatch_once保证多线程情况下获取的仍然是同一个对象,我们还可以使用同步锁的方式达到这个目的,代码如下:

    + (instancetype)shareInstance {
        static ALYNetworkManager *instance = nil;
        instance = [[ALYNetworkManager alloc] init];
       
        return instance;
    }
    
    static ALYNetworkManager *_instance = nil;
    + (instancetype)allocWithZone:(struct _NSZone *)zone {
        @synchronized (self) {
            if (!_instance) {
                _instance = [super allocWithZone:zone];
            }
        }
        return _instance;
    }
    

    验证多线程下获取的是否为同一个实例,如下图:


    方式四

    当然还可以在单例的全局访问点(类方法)中使用同步锁,其结果和方式二是一样的:外部都不能直接通过调用[[class alloc] init]方法来获取单例,代码如下:

    + (instancetype)shareInstance {
        static ALYNetworkManager *instance = nil;
        @synchronized (self) {
            if (!instance) {
                instance = [[ALYNetworkManager alloc] init];
            }
        }
        return instance;
    }
    

    单例工具类的创建

    此处以利用dispatch_once一次性代码 + 宏定义的方式抽取出一个通用的单例宏,其实在网上随便一搜,就可以搜到一大把单例宏。

    ARC\MRC判断

    #if __has_feature(objc_arc)
    
    #else
    
    #endif
    

    宏抽取

    #define interfaceSingleton(name)  +(instancetype)share##name
    
    
    #if __has_feature(objc_arc)
    // ARC
    #define implementationSingleton(name)  \
    + (instancetype)share##name \
    { \
    name *instance = [[self alloc] init]; \
    return instance; \
    } \
    static name *_instance = nil; \
    + (instancetype)allocWithZone:(struct _NSZone *)zone \
    { \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
    _instance = [[super allocWithZone:zone] init]; \
    }); \
    return _instance; \
    } \
    - (id)copyWithZone:(NSZone *)zone{ \
    return _instance; \
    } \
    - (id)mutableCopyWithZone:(NSZone *)zone \
    { \
    return _instance; \
    }
    #else
    // MRC
    
    #define implementationSingleton(name)  \
    + (instancetype)share##name \
    { \
    name *instance = [[self alloc] init]; \
    return instance; \
    } \
    static name *_instance = nil; \
    + (instancetype)allocWithZone:(struct _NSZone *)zone \
    { \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
    _instance = [[super allocWithZone:zone] init]; \
    }); \
    return _instance; \
    } \
    - (id)copyWithZone:(NSZone *)zone{ \
    return _instance; \
    } \
    - (id)mutableCopyWithZone:(NSZone *)zone \
    { \
    return _instance; \
    } \
    - (oneway void)release \
    { \
    } \
    - (instancetype)retain \
    { \
    return _instance; \
    } \
    - (NSUInteger)retainCount \
    { \
    return  MAXFLOAT; \
    }
    #endif
    
    

    以后如果需要创建单例工具类直接创建一个Singleton.h文件,把上面的代码拷贝到.h文件中。在单例类(拿Person类为例)中导入这个头文件。直接在单例类的.h文件中interfaceSingleton(name)传入参数,.m文件中implementationSingleton(name)传入参数即可。

    如下:

    /************ALYNetworkManager.h**********/
    #import <Foundation/Foundation.h>
    #import "Singleton.h"
    
    @interface ALYNetworkManager : NSObject
    
    interfaceSingleton(ALYNetworkManager);
    
    @end
    
    /************ALYNetworkManager.m**********/
    #import "ALYNetworkManager.h"
    
    @implementation ALYNetworkManager
    
    
    implementationSingleton(ALYNetworkManager)
    
    @end
    
    

    git地址点这里

    文/VV木公子(简书作者)
    PS:如非特别说明,所有文章均为原创作品,著作权归作者所有,转载请联系作者获得授权,并注明出处,所有打赏均归本人所有!

    如果您是iOS开发者,或者对本篇文章感兴趣,请关注本人,后续会更新更多相关文章!敬请期待!

    相关文章

      网友评论

        本文标题:浅析单例设计模式

        本文链接:https://www.haomeiwen.com/subject/saxzvttx.html