OC單例模式詳解
阿新 • • 發佈:2018-12-09
單例模式
有時候我們需要一個全域性的物件,而且要保證全域性有且僅有一份即可,這時候就需要用到單例設計模式,但是需要注意的是:在多執行緒的環境下也需要做好執行緒保護。其實系統已經有很多單例存在,例如UIApplication、NSNotification、NSFileManager、NSUserDefaults等.以下程式碼詳解
ARC環境下嚴謹的單例模式
#import <Foundation/Foundation.h> @interface JHTool : NSObject<NSCopying,NSMutableCopying> //類方法 //1.方便訪問 //2.標明身份 //3.注意:share+類名|default + 類名 | share | default | 類名 +(instancetype)shareTool; @end
#import "JHTool.h" @implementation JHTool //提供一個全域性靜態變數 static JHTool * _instance; +(instancetype)shareTool{ return [[self alloc]init]; } //當呼叫alloc的時候會呼叫allocWithZone +(instancetype)allocWithZone:(struct _NSZone *)zone{ //方案一:加互斥鎖,解決多執行緒訪問安全問題 // @synchronized(self){//同步的 // if (!_instance) { // _instance = [super allocWithZone:zone]; // } // } //方案二.GCD dispatch_onec,本身是執行緒安全的,保證整個程式中只會執行一次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; } //嚴謹 //遵從NSCopying協議,可以通過copy方式建立物件 - (nonnull id)copyWithZone:(nullable NSZone *)zone { return _instance; } //遵從NSMutableCopying協議,可以通過mutableCopy方式建立物件 - (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
列印結果:
MRC環境下嚴謹的單例模式
Xcode5以後專案預設都是ARC,所以把專案設定成MRC環境,選擇專案 Target -> Build Sttings -> All -> 搜尋‘Automatic’ -> 把 Objective-C Automatic Reference Counting 設定為 NO ,如下圖: MRC單例模式程式碼詳解
#import <Foundation/Foundation.h>
@interface HJTool : NSObject<NSCopying,NSMutableCopying>
//類方法
+(instancetype)shareTool;
@end
#import "HJTool.h"
@implementation HJTool
//修改環境為MRC:選擇專案 Target -> Build Sttings -> All -> 搜尋‘Automatic’ -> 把 Objective-C Automatic Reference Counting 設定為 NO
//提供一個靜態全域性變數
static HJTool * _instance;
//實現類方法
+(instancetype)shareTool{
return [[self alloc]init];
}
//alloc會呼叫allocWithZone
+(instancetype)allocWithZone:(struct _NSZone *)zone{
//方法一.互斥鎖保證執行緒安全
// @synchronized(self){
// if (_instance == nil) {
// _instance = [super allocWithZone:zone];
// }
// }
//方法一.GCD-> dispatch_once_t 該方法只會執行一次,本身執行緒安全
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);
}
列印結果
拓展:區分是MRC還是ARC的巨集
#if __has_feature(objc_arc)
//條件滿足 ARC,可以處理ARC的程式碼
NSLog(@"ARC");
#else
//條件滿足 MRC,,可以處理MRC的程式碼
NSLog(@"MRC");
#endif
通用的單例模式
在
SingleDog.h
檔案中定義一個巨集,SingleH(name)
定義單例.h檔案的類方法宣告,SingleM(name)
定義和實現單例.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
檔案,標頭檔案和實現檔案分別呼叫巨集的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環境下列印結果如下: