1. 程式人生 > >OC單例模式詳解

OC單例模式詳解

單例模式

有時候我們需要一個全域性的物件,而且要保證全域性有且僅有一份即可,這時候就需要用到單例設計模式,但是需要注意的是:在多執行緒的環境下也需要做好執行緒保護。其實系統已經有很多單例存在,例如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

列印結果: ARC

MRC環境下嚴謹的單例模式

Xcode5以後專案預設都是ARC,所以把專案設定成MRC環境,選擇專案 Target -> Build Sttings -> All -> 搜尋‘Automatic’ -> 把 Objective-C Automatic Reference Counting 設定為 NO ,如下圖: 專案設定成MRC環境 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環境下列印結果如下: 

MRCéç¨åä¾æ¨¡å¼