iOS之单例设计模式

iOS开发多线程篇—单例模式(ARC\MRC)

  1. 简单说明:

设计模式:多年软件开发,总结出来的一套经验、方法和工具,或者是是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

在iOS中最常用的是单例模式和代理模式,下面我们分析和总结一下单利模式。

  1. 单例模式说明
  • 单例模式的作用 :可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问,从而方便地控制了实例个数,并节约系统资源。
  • 单例模式的使用场合:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次),应该让这个类创建出来的对象永远只有一个。
  • 单例模式在ARC\MRC环境下的写法有所不同,需要编写2套不同的代码
  1. 创建单例

单例又分两种即懒汉式饿汉式

  • 懒汉式:第一次用到单利对象时,再创建。
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 <Foundation/Foundation.h>
@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 <Foundation/Foundation.h>
@interface HMMovieTool : NSObject
HMSingletonH(MovieTool)
@end
.m
#import "TKStudent.h"
@implementation TKStudent
HMSingletonM(MovieTool)
@end 
小补充:一个类中两个方法的调用顺序差别
+(void)load{}
+(void)initialize()
load方法是在加载时调用,即在该类在装载进内存时就会执行,
initialize方法是在一个类在其第一次被调用或其子类被调用的时候才会执行。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容