单例设计模式
单例工具类的创建
1.利用一次性代码
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ <#code to be executed once#> });
2.不可以通过继承的方式,使子类成为单例。如果继承,会引发如下两个问题
- 如果先创建父类,那么子类创建出来的对象也永远是父类
- 如果先创建子类,那么父类创建出来的对象也永远是子类
3.宏定义抽取单例:
方法的声明
方法的实现
用以下方法判断是否是ARC
#if __has_feature(objc_arc) #else #endif
// 以后就可以使用interfaceSingleton来替代后面的方法声明
#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)传入参数即可。
如下:
#import <Foundation/Foundation.h> #import "Singleton.h" @interface Person : NSObject interfaceSingleton(Person); @end
#import "Person.h" @implementation Person implementationSingleton(Person) @end
创建单例的几种方式
方式一:传统且正规的方法是把一次性代码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; }
代码地址:https://github.com/nlgb/SingletonDemo