前言
前段時(shí)間和同事交流單例模式,感覺(jué)自己對(duì)單例了解還有很多不足。上網(wǎng)查了下發(fā)現(xiàn),有些帖子太坑,拿著偽單例大書(shū)特書(shū),看著眼疼。
單例不是簡(jiǎn)單的編程技巧,它是編程模式,能上升到模式級(jí)別的東西,不是說(shuō)幾行簡(jiǎn)單代碼能搞定的,每次聽(tīng)到“單例模式是設(shè)計(jì)模式中最簡(jiǎn)單的形式”這句話(huà)時(shí),都有種想拿人撞墻的感覺(jué)。
什么是單例模式
提到單例,如果在大學(xué)里上Java課的時(shí)候,你忘了睡覺(jué),那么你肯定知道單例有很多種寫(xiě)法,比如:餓漢式、懶漢式等。單例類(lèi)的寫(xiě)法也是很多公司,筆試必考題之一。在iOS系統(tǒng)為我們提供了許多單例類(lèi),如,UIApplication、NSUserDefaults等。
單例模式是一個(gè)Class在系統(tǒng)中只允許生成一個(gè)Instance Object,以共享內(nèi)存的方式供系統(tǒng)中其他實(shí)例對(duì)象進(jìn)行訪問(wèn)。在iOS開(kāi)發(fā)中,單例模式是非常有用的一種設(shè)計(jì)模式。
提到單例,很多人都會(huì)舉一個(gè)例子,
具體場(chǎng)景:一臺(tái)機(jī)器多個(gè)可打印的應(yīng)用Pro1、 Pro2、Pro3......,一臺(tái)打印機(jī)Print;
在多個(gè)應(yīng)用連接打印機(jī)的時(shí)候,Pro1、 Pro2、Pro3連接打印機(jī)Print的時(shí)候,生成的Print實(shí)例是同一個(gè)實(shí)例。
單例的作用
- 單例類(lèi)保證了應(yīng)用程序的生命周期中有且僅有一個(gè)該類(lèi)的實(shí)例對(duì)象,而且易于外界訪問(wèn)。
- 共享內(nèi)存,存儲(chǔ)用戶(hù)數(shù)據(jù)等
- 無(wú)交互類(lèi)傳值時(shí),充當(dāng)中間人
寫(xiě)法分類(lèi):
- 懶漢式 : 首次用到單例對(duì)象的時(shí),創(chuàng)建實(shí)例對(duì)象;
- 餓漢式 : 進(jìn)入程序就創(chuàng)建實(shí)例對(duì)象
提到這兩種方式,首先說(shuō)個(gè)知識(shí)點(diǎn):NSObject的load和initialize方法,點(diǎn)開(kāi)Apple官方關(guān)于NSObject的API會(huì)發(fā)現(xiàn),在NSObject.h文件中有兩個(gè)類(lèi)方法load、initialize,
@interface NSObject <NSObject>
{
Class isa OBJC_ISA_AVAILABILITY;
}
+ (void)load;
+ (void)initialize;
- + (void)load 官方描述
The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.
The order of initialization is as follows:
All initializers in any framework you link to.
All +load methods in your image.
All C++ static initializers and C/C++ attribute(constructor) functions in your image.
All initializers in frameworks that link to you.
- + (void) initialize 官方描述
The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses. The superclass implementation may be called multiple times if subclasses do not implement initialize—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize].
Apple的官方文檔清楚地說(shuō)明了initialize和load的區(qū)別:
load:只要類(lèi)所在文件被引用就會(huì)被調(diào)用,即文件的.m文件引入,系統(tǒng)運(yùn)行,load就會(huì)被執(zhí)行;
initialize:在類(lèi)或者其子類(lèi)的方法被調(diào)用前被調(diào)用,它屬于懶漢的,自己的方法沒(méi)被用到,它是不會(huì)執(zhí)行的。
相同點(diǎn):兩種方法都只會(huì)被調(diào)用一次。
為了防止不停的if -->( _instance == nil) else ....這種令人作嘔的判斷,下面實(shí)現(xiàn)使用線程方式(記得實(shí)現(xiàn)NSCopying, NSMutableCopying協(xié)議方法):
先驗(yàn)證下,哥的單例是偽的,還是真的,有圖為證:
有朋友問(wèn)到,使用alloc創(chuàng)建實(shí)例為什么會(huì)地址相同,主要是因?yàn)橹貙?xiě)了allocWithZone,返回同一個(gè)實(shí)例。后面mutableCopyWithZone和copyWithZone方法的重寫(xiě),是即便是使用copy修飾,也同樣返回同一個(gè)實(shí)例。
@property (nonatomic, copy) EVNHelper *helper;
懶漢式單例
.h文件:
//
// EVNHelper.h
// MMBao_master
//
// Created by developer on 16/6/11.
// Copyright ? 2016年 仁伯安. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface EVNHelper : NSObject<NSCopying, NSMutableCopying>
+ (instancetype)shareInstance;
@end
.m文件:
//
// EVNHelper.m
// MMBao_master
//
// Created by developer on 16/6/11.
// Copyright ? 2016年 仁伯安. All rights reserved.
//
#import "EVNHelper.h"
static id _instance;
@implementation EVNHelper
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)shareInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
@end
餓漢式單例
.h文件:
//
// EVNHelper.h
// MMBao_master
//
// Created by developer on 16/6/11.
// Copyright ? 2016年 仁伯安. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface EVNHelper : NSObject<NSCopying, NSMutableCopying>
+ (instancetype)shareInstance;
@end
.m文件:
//
// EVNHelper.m
// MMBao_master
//
// Created by developer on 16/6/11.
// Copyright ? 2016年 仁伯安. All rights reserved.
//
#import "EVNHelper.h"
static id _instance;
@implementation EVNHelper
/**
* 只要系統(tǒng)中引用了該類(lèi),程序運(yùn)行,就會(huì)主動(dòng)調(diào)用load(不用手動(dòng)調(diào)用,而且只會(huì)加載1次)
*/
+ (void)load
{
_instance = [[EVNHelper alloc] init]; //[[self alloc] init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)shareInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
@end
上面的寫(xiě)法是在ARC情況下,MRC還需在.m文件中添加如下方法:(鄙人不太懂MRC,只為擴(kuò)充下,有問(wèn)題,請(qǐng)回復(fù),指教)
(1) 重寫(xiě)release方法為空
(2) 重寫(xiě)retain方法返回self
(3) 重寫(xiě)retainCount返回 1
(4) 重寫(xiě)autorelease返回self
- (oneway void)release
{
NSLog(@"這里空語(yǔ)句就行");
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return 1;
}
- (id)autorelease
{
return self;
}
本文已在版權(quán)印備案,如需轉(zhuǎn)載請(qǐng)?jiān)诎鏅?quán)印獲取授權(quán)。
獲取版權(quán)