OC單例模式詳解

單例模式

有時(shí)候我們需要一個(gè)全局的對(duì)象葬荷,而且要保證全局有且僅有一份即可,這時(shí)候就需要用到單例設(shè)計(jì)模式,但是需要注意的是:在多線程的環(huán)境下也需要做好線程保護(hù)。其實(shí)系統(tǒng)已經(jīng)有很多單例存在,例如UIApplication娜膘、NSNotification痕届、NSFileManager、NSUserDefaults等.以下代碼詳解

ARC環(huán)境下嚴(yán)謹(jǐn)?shù)膯卫J?/h4>
#import <Foundation/Foundation.h>
@interface JHTool : NSObject<NSCopying,NSMutableCopying>
//類方法
//1.方便訪問(wèn)
//2.標(biāo)明身份
//3.注意:share+類名|default + 類名 | share | default | 類名
+(instancetype)shareTool;
@end
#import "JHTool.h"
@implementation JHTool
//提供一個(gè)全局靜態(tài)變量
static JHTool * _instance;

+(instancetype)shareTool{
    return [[self alloc]init];
}

//當(dāng)調(diào)用alloc的時(shí)候會(huì)調(diào)用allocWithZone
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    //方案一:加互斥鎖,解決多線程訪問(wèn)安全問(wèn)題
//    @synchronized(self){//同步的
//        if (!_instance) {
//            _instance = [super allocWithZone:zone];
//        }
//    }
    //方案二.GCD dispatch_onec,本身是線程安全的,保證整個(gè)程序中只會(huì)執(zhí)行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}
//嚴(yán)謹(jǐn)
//遵從NSCopying協(xié)議,可以通過(guò)copy方式創(chuàng)建對(duì)象
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
    return _instance;
}
//遵從NSMutableCopying協(xié)議,可以通過(guò)mutableCopy方式創(chuàng)建對(duì)象
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
    return _instance;
}
@end
#import "ViewController.h"
#import "JHTool.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
    JHTool * tool1 = [[JHTool alloc]init];
    JHTool * tool2 = [JHTool shareTool];
    JHTool * tool3 = tool1.copy;
    JHTool * tool4 = tool2.mutableCopy;
    NSLog(@"tool1:%p,tool2:%p,tool3:%p,tool4:%p,",tool1,tool2,tool3,tool4);
    printf("tool1:%p,tool2:%p,tool3:%p,tool4:%p,",tool1,tool2,tool3,tool4);
    
}
@end

打印結(jié)果:


ARC單例模式

MRC環(huán)境下嚴(yán)謹(jǐn)?shù)膯卫J?/h4>

Xcode5以后項(xiàng)目默認(rèn)都是ARC,所以把項(xiàng)目設(shè)置成MRC環(huán)境,選擇項(xiàng)目 Target -> Build Sttings -> All -> 搜索‘Automatic’ -> 把 Objective-C Automatic Reference Counting 設(shè)置為 NO 徘公,如下圖:


項(xiàng)目設(shè)置成MRC環(huán)境

MRC單例模式代碼詳解

#import <Foundation/Foundation.h>
@interface HJTool : NSObject<NSCopying,NSMutableCopying>
//類方法
+(instancetype)shareTool;
@end
#import "HJTool.h"

@implementation HJTool
//修改環(huán)境為MRC:選擇項(xiàng)目 Target -> Build Sttings -> All -> 搜索‘Automatic’ -> 把 Objective-C Automatic Reference Counting 設(shè)置為 NO
//提供一個(gè)靜態(tài)全局變量
static HJTool * _instance;
//實(shí)現(xiàn)類方法
+(instancetype)shareTool{
    return [[self alloc]init];
}
//alloc會(huì)調(diào)用allocWithZone
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    //方法一.互斥鎖保證線程安全
//    @synchronized(self){
//        if (_instance == nil) {
//            _instance = [super allocWithZone:zone];
//        }
//    }
    //方法一.GCD-> dispatch_once_t 該方法只會(huì)執(zhí)行一次,本身線程安全
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}
-(id)copyWithZone:(NSZone *)zone{
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
    return _instance;
}
//MRC特有的
-(instancetype)retain{
    return _instance;
}
-(oneway void)release{
    NSLog(@"%zd",_instance.retainCount);
}
#import "ViewController.h"
#import "HJTool.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor yellowColor];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    HJTool * t1 = [[HJTool alloc]init];
    HJTool * t2 = [[HJTool alloc]init];
    HJTool * t3 = [HJTool shareTool];
    HJTool * t4 = [t1 copy];
    HJTool * t5 = [t2 mutableCopy];
    HJTool * t6 = [t1 retain];
    NSLog(@"t6.retainCount : %zd",t1.retainCount);
    NSLog(@"t1:%p t2:%p t3:%p t4:%p t5:%p t6:%p",t1,t2,t3,t4,t5,t6);
}

打印結(jié)果


MRC單例模式

拓展:區(qū)分是MRC還是ARC的宏

#if __has_feature(objc_arc)
    //條件滿足 ARC,可以處理ARC的代碼
    NSLog(@"ARC");
#else
    //條件滿足 MRC,,可以處理MRC的代碼
    NSLog(@"MRC");
#endif

通用的單例模式

SingleDog.h文件中定義一個(gè)宏, SingleH(name)定義單例.h文件的類方法聲明,SingleM(name)定義和實(shí)現(xiàn)單例.m文件的方法,定義的宏的代碼如下

#define SingleH(name) +(instancetype)share##name;//.h文件替換
//.m文件替換
#if __has_feature(objc_arc)//arc
//條件滿足 ARC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}

#else
//條件滿足 MRC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
return _instance;\
}
#endif

單例SingleTool文件,頭文件和實(shí)現(xiàn)文件分別調(diào)用宏的SingleH(name),SingleM(name)即可,代碼如下

#import <Foundation/Foundation.h>
#import "SingleDog.h"

@interface SingleTool : NSObject
//替換頭文件
SingleH(SingleTool)

@end
#import "SingleTool.h"

@implementation SingleTool
SingleM(SingleTool)
@end
#import "ViewController.h"
#import "SingleTool.h"

@interface ViewController ()

@end

@implementation ViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    SingleTool * t1 = [[SingleTool alloc]init];
    SingleTool * t2 = [SingleTool shareSingleTool];
    SingleTool * t3 = [t1 copy];
    SingleTool * t4 = [t1 mutableCopy];
#if __has_feature(objc_arc)
    //條件滿足 ARC,可以處理ARC的代碼
    NSLog(@"ARC");
#else
    //條件滿足 MRC,,可以處理MRC的代碼
    NSLog(@"MRC");
    SingleTool * t5 = [t1 retain];
    NSLog(@"t6.retainCount : %zd",t1.retainCount);
    NSLog(@"t1:%p t2:%p t3:%p t4:%p t5:%p",t1,t2,t3,t4,t5);
#endif
}

@end

MRC環(huán)境下打印結(jié)果如下:


MRC通用單例模式
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末牲证,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子关面,更是在濱河造成了極大的恐慌坦袍,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件等太,死亡現(xiàn)場(chǎng)離奇詭異捂齐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)缩抡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門奠宜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瞻想,你說(shuō)我怎么就攤上這事挎塌。” “怎么了内边?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵榴都,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我漠其,道長(zhǎng)嘴高,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任和屎,我火速辦了婚禮拴驮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柴信。我一直安慰自己套啤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布随常。 她就那樣靜靜地躺著潜沦,像睡著了一般萄涯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唆鸡,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天涝影,我揣著相機(jī)與錄音,去河邊找鬼争占。 笑死燃逻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的臂痕。 我是一名探鬼主播伯襟,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼握童!你這毒婦竟也來(lái)了姆怪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舆瘪,失蹤者是張志新(化名)和其女友劉穎片效,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體英古,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淀衣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了召调。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膨桥。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖唠叛,靈堂內(nèi)的尸體忽然破棺而出只嚣,到底是詐尸還是另有隱情,我是刑警寧澤艺沼,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布册舞,位于F島的核電站,受9級(jí)特大地震影響障般,放射性物質(zhì)發(fā)生泄漏调鲸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一挽荡、第九天 我趴在偏房一處隱蔽的房頂上張望藐石。 院中可真熱鬧,春花似錦定拟、人聲如沸于微。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)株依。三九已至驱证,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勺三,已是汗流浹背雷滚。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工需曾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吗坚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓呆万,卻偏偏與公主長(zhǎng)得像商源,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谋减,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容