
最近我面試人家,問他對(duì)單例的認(rèn)識(shí),他對(duì)單例的基本認(rèn)識(shí)、創(chuàng)建方式回答全對(duì),只有一點(diǎn)吞吞吐吐。
單例的認(rèn)識(shí)
單例模式:?jiǎn)卫J绞窃O(shè)計(jì)模式中最簡(jiǎn)單的形式之一。這一模式的目的是使得類的一個(gè)對(duì)象成為系統(tǒng)中的唯一實(shí)例
系統(tǒng)單例:
1、UIApplication(應(yīng)用程序?qū)嵗?
2、NSNotificationCenter(消息中心):
3、NSFileManager(文件管理):
4、NSUserDefaults(應(yīng)用程序設(shè)置):
5、NSURLCache(請(qǐng)求緩存):
6、NSHTTPCookieStorage(應(yīng)用程序cookies池):
1.單例模式的要點(diǎn):
顯然單例模式的要點(diǎn)有三個(gè);一是某個(gè)類只能有一個(gè)實(shí)例;二是它必須自行創(chuàng)建這個(gè)實(shí)例;三是它必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。
2.單例模式的優(yōu)點(diǎn):
1.安全性和唯一性:Singleton 會(huì)阻止其他對(duì)象實(shí)例化其自己的 Singleton 對(duì)象的副本,從而確保所有對(duì)象都訪問唯一實(shí)例。單例類也可以防止他人復(fù)制(copy),保留(retain)或釋放(release)實(shí)例。如果您發(fā)現(xiàn)需要,您可以創(chuàng)建自己的單例。例如,如果您有一個(gè)類為應(yīng)用程序中的其他對(duì)象提供聲音,則可以將其設(shè)為單例。
2.靈活性:因?yàn)轭惪刂屏藢?shí)例化過程,所以類可以更加靈活修改實(shí)例化過程
創(chuàng)建步驟
1、為你的單例類聲明一個(gè)靜態(tài)的實(shí)例(聲明靜態(tài)全局變量),并且初始化它的值為nil; eg:
static TestSingleton *testSingleton = nil;
這樣,在獲取實(shí)例的方法中,只有在靜態(tài)實(shí)例為nil的時(shí)候,產(chǎn)生一個(gè)你的類的實(shí)例,這個(gè)實(shí)例通常被稱為共享的實(shí)例;
2、重寫allocWithZone方法,用于確定:不能夠使用其他的方法創(chuàng)建我們類的實(shí)例,限制用戶只能通過獲取實(shí)例的方法得到這個(gè)類的實(shí)例。所以我們?cè)赼llocWithZone方法中直接返回共享的類實(shí)例;
3、寫+(instancetype)shareSingleton的函數(shù)體
創(chuàng)建方法
一、傳統(tǒng)方法
+(instancetype)shareSingleton{
static Singleton *singleton = nil;
if (singleton == nil){
singleton = [[self alloc] init];
}
return singleton;
}
二、推薦方法(GCD)
+(instancetype)shareSingleton{
static Singleton *singleton = nil;
//給單例加一個(gè)線程鎖
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[Singleton alloc] init];
});
return singleton;
三、線程加鎖方法(不推薦使用)
+(instancetype)shareSingleton{
static Singleton *singleton = nil;
@synchronized(self) {
singleton = [[Singleton alloc] init];
}
return singleton;
}
注:“線程加鎖方法”這樣性能不是很好,因?yàn)槊看握{(diào)用+ (instancetype)sharedSingleton函數(shù)都會(huì)付出取鎖的代價(jià)
吞吞吐吐:
但我問他,這樣寫的話是保證了線程安全,但通過自帶的<code>alloc</code>或者<code>new</code>來進(jìn)行實(shí)例化,還是不能保證該對(duì)象只被創(chuàng)建一次,如何避免呢?他就回答不上了,其實(shí)很簡(jiǎn)單:
- .h頭文件:
@interface TestSingleton : NSObject
@property (nonatomic, copy)NSString *testStr;
+ (TestSingleton *)shareinstance;
@end
- .m文件:
+ (TestSingleton *)shareinstance {
static TestSingleton *testSingleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//testSingleton = [self singletonAlloc]; //后面使用該創(chuàng)建方法
testSingleton = [self new];//[[self alloc]init];
});
return testSingleton;
}
//我們需要重載<code>alloc、new</code>方法
+ (instancetype)singletonAlloc
{
return [super alloc];
}
+ (instancetype)alloc
{
//加斷言,使用alloc會(huì)蹦,并且reason提示
NSAssert(NO, @"不要用alloc,要使用singletonAlloc方法創(chuàng)建");
return nil;
}
+ (instancetype)new
{
return [self alloc];
}
+ (TestSingleton *)shareinstance {
static TestSingleton *testSingleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
testSingleton = [[self singletonAlloc]init];
});
return testSingleton;
}
重載了alloc、allocWithZone和new方法,在alloc方法中加上斷言來提醒,讓用戶不能使用alloc創(chuàng)建實(shí)例。
擴(kuò)展一:(面試經(jīng)常問到)

普通類允許調(diào)用者根據(jù)需要?jiǎng)?chuàng)建盡可能多的類的實(shí)例,而對(duì)于單例類,每個(gè)進(jìn)程只能有一個(gè)類的實(shí)例,保證了單例數(shù)據(jù)的唯一性,安全性
調(diào)用下面方法:
-(void)testSigleton
{
/**
* singleton和singleton2是同一個(gè)對(duì)象;
*/
TestSingleton *singleton = [TestSingleton shareinstance];
TestSingleton *singleton2 = [TestSingleton shareinstance];
if (singleton == singleton2) {
NSLog(@"singleton == singleton2");
}
NSLog(@"singleton地址:%@",singleton);
NSLog(@"singleton2地址:%@",singleton2);
}
打印如下
SingletonTest[1598:54880] singleton == singleton2
SingletonTest[1598:54880] singleton地址:<TestSingleton: 0x60000001e930>
SingletonTest[1598:54880] singleton2地址:<TestSingleton: 0x60000001e930>
可以看出地址一樣,證明單例只會(huì)創(chuàng)建一個(gè)靜態(tài)變量,全局唯一
擴(kuò)展二:(宏定義單例)
作用:有時(shí)在項(xiàng)目中需要?jiǎng)?chuàng)建好多個(gè)單例,把單例的代碼定義為宏,則可以省去重復(fù)代碼,節(jié)省時(shí)間。
/**
* 在.h文件中定義的宏,arc
*
* DWSingletonH(classname, accessorMethodName) 這個(gè)是宏
* 在外邊我們使用 “DWSingletonH(classname, accessorMethodName)” 那么在.h文件中,定義了一個(gè)方
法"+ (instancetype)accessorMethodName;"
*
*/
#define DWSingletonH(classname, accessorMethodName) + (instancetype)accessorMethodName;
/**
* 在.m文件中處理好的宏 arc
*
* DWSingletonM(classname, accessorMethodName) 這個(gè)是宏,因?yàn)槭嵌嘈械臇|西,所以每行后面都有一個(gè)"\",最后一行除外
*/
#define DWSingletonM(classname, accessorMethodName) \
static classname *instance_ = nil;\
+ (instancetype)accessorMethodName{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance_ = [[self alloc] init];\
});\
return instance_;\
}\
+ (instancetype)allocWithZone:(struct _NSZone *)zone{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance_ = [super allocWithZone:zone];\
});\
return instance_;\
}\
- (id)copyWithZone:(NSZone *)zone{\
return instance_;\
}
把上面代碼用一個(gè)<code>Header File</code>文件里面,然后就可以在其他類快速創(chuàng)建單例了
- .h頭文件:
@interface Singleton2 : NSObject
//@property double t;
DWSingletonH(Singleton2, shareInstance)
@end
- .m文件:
@implementation Singleton2
DWSingletonM(Singleton2, shareInstance)
@end