iOS开发多线程篇—单例模式(ARC\MRC)
-
简单说明:
设计模式:多年软件开发,总结出来的一套经验、方法和工具,或者是是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
在iOS中最常用的是单例模式和代理模式,下面我们分析和总结一下单利模式。
-
单例模式说明
- 单例模式的作用 :可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问,从而方便地控制了实例个数,并节约系统资源。
- 单例模式的使用场合:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次),应该让这个类创建出来的对象永远只有一个。
- 单例模式在ARC\MRC环境下的写法有所不同,需要编写2套不同的代码
-
创建单例
单例又分两种即懒汉式
和饿汉式
。
- 懒汉式:第一次用到单利对象时,再创建。
static id _instance;
+ (id)allocWithZone:(struct _NSZone *)zone {`
if (_instance == nil) { // 防止频繁加锁
@synchronized(self) { //防止多线程并发同时访问,需加锁
if (_instance == nil) { // 防止创建多次
_instance = [super allocWithZone:zone];
}
}
}
return _instance;
}
+ (instancetype)sharedMusicTool
{
if (_instance == nil) { // 防止频繁加锁
@synchronized(self) {//防止多线程并发同时访问,需加锁
if (_instance == nil) { // 防止创建多次
_instance = [[self alloc] init];
}
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
- 饿汉式:一进入程序就创建一个单例对象。
static id _instance;
/**
* 当类加载到OC运行时环境中(内存),就会调用一次(一个类只会加载1次)
*/
+ (void)load
{
_instance = [[self alloc] init];
}
+ (id)allocWithZone:(struct _NSZone *)zone
{
if (_instance == nil) { // 防止创建多次
_instance = [super allocWithZone:zone];
}
return _instance;
}
+ (instancetype)sharedSoundTool
{
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
上面已经提到,懒汉式和饿汉式的区别,即懒汉式和饿汉式创建单例的区别在于懒汉式是第一次用到单利对象时,再创建。饿汉式是一进入程序就创建一个单例对象。
再者由于饿汉式是在该类在第一次创建的时候就就创建一个单利对象,所以该类肯定是第一次被访问,不用考虑线程并发,同时访问,所以就不用加锁。
我们平常都是用懒汉式,基本上不适用饿汉式。
上面提到创建懒汉式,需要加锁,那么我们可以使用GCD的dispatch_once方法,即该模块中的方法在程序运行时只执行一次,而且自带互斥锁,用该方法会使创建更加便捷如下。
static id _instace;
+ (id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [super allocWithZone:zone];
});
return _instace;
}
+ (instancetype)sharedDataTool
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [[self alloc] init];
});
return _instace;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instace;
}
由于创建单利的方法都相同,所以我可以把创建单利所需的几个方法抽成宏,那在创建单利的文件中导入该宏即可完成单利类的创建。
// .h文件
#define HMSingletonH + (instancetype)sharedInstance;
// .m文件
#define HMSingletonM \
static id _instance; \
\
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)sharedInstance \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instance; \
}
我们上面提到的是ARC的中单利类的创建,MRC和ARC差不多,只是需要重写下面四个方法,即把所用手动更改内存和引用计数的计数放到自己自己类文件中,防止外部更改该单利的内存或引用计数而造成内存非一份的情况。
- (oneway void)release { }
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1;}
- (id)autorelease { return self;}
static id _instace;
+ (id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [super allocWithZone:zone];
});
return _instace;
}
+ (instancetype)sharedDataTool
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [[self alloc] init];
});
return _instace;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instace;
}
- (oneway void)release { }
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1;}
- (id)autorelease { return self;}
MRC单利宏如下
// .h文件
#define HMSingletonH + (instancetype)sharedInstance;
// .m文件
#define HMSingletonM \
static id _instance; \
\
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)sharedInstance \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instance; \
}\
- (oneway void)release { }\
- (id)retain { return self; }\
- (NSUInteger)retainCount { return 1;}\
- (id)autorelease { return self;}
例:创建时只需导入宏即可
.h文件
#import
@interface HMMovieTool : NSObject
HMSingletonH(MovieTool)
@end
.m文件
#import "TKStudent.h"
@implementation TKStudent
HMSingletonH
@end
补充现在是用[class sharedInstance]的固定模式和固定代码实现的,如果想使用类的名字可以在宏文件中改点东西,即拼接宏文件名(在宏文件中拼接需要使用##
号)如下
// .h文件
#define HMSingletonH(name) + (instancetype)shared##(name);
// .m文件
#define HMSingletonM(name) \
static id _instance; \
\
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)shared##name \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instance; \
}
例:创建时和上面稍有不同,只需导入宏+(文件名)即可
.h
#import
@interface HMMovieTool : NSObject
HMSingletonH(MovieTool)
@end
.m
#import "TKStudent.h"
@implementation TKStudent
HMSingletonM(MovieTool)
@end
小补充:一个类中两个方法的调用顺序差别
+(void)load{}
+(void)initialize()
load方法是在加载时调用,即在该类在装载进内存时就会执行,
initialize方法是在一个类在其第一次被调用或其子类被调用的时候才会执行。