ps:所謂單例,就是在程序中只有一個(gè)實(shí)例胶坠,它提供一個(gè)類方法供全局調(diào)用。在編譯時(shí)初始化這個(gè)類繁堡,然后保存在內(nèi)存中沈善,在程序退出時(shí),由系統(tǒng)自動釋放這部分內(nèi)存椭蹄。系統(tǒng)為我們提供的單例有:UIApplication闻牡、NSNotificationCenter、NSFileManager绳矩、NSUserDefaults罩润、NSURLCache、NSHTTPCookieStorage等翼馆。
創(chuàng)建一個(gè)單例時(shí),需要注意的是:實(shí)例化一個(gè)類有多種方法写妥,alloc拳球、new、allocWithZone,你寫的初始化單例的類方法 sharedInstance祝峻,但你不能保證你的同伴不會調(diào)用其他初始化方法去初始化這個(gè)類,因此你需要將其余的初始化方法全部干掉辞色,保證不管用什么初始化方法創(chuàng)建的實(shí)例浮定,都有一個(gè)共同的內(nèi)存地址相满。
+(instancetype)sharedInstance
{
static Person *p = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
p = [[Person alloc]init];
});
return p;
}
Person *p1 = [[Person alloc]init];
Person *p2 = [Person new];
Person *p0 = [Person sharedInstance];
Person *p = [Person sharedInstance];
NSLog(@"\n%@==\n%@==\n%@==\n%@",p1,p2,p0,p);
log:
2018-12-04 17:02:32.057731+0800 OCTest[46730:8876108]
<Person: 0x600003f36d10>==
<Person: 0x600003f36da0>==
<Person: 0x600003f36db0>==
<Person: 0x600003f36db0>
像上面這樣顯然是不可取的,不同初始化方法初始化的對象的內(nèi)存不是同一個(gè)建蹄。
創(chuàng)建單例方法一:重寫allocWithZone
OC初始化一個(gè)對象分為兩步:[[Class alloc]init]
1嘿棘,給對象分配內(nèi)存空間(alloc)
2鸟妙,初始化對象的相關(guān)信息 (init)
當(dāng)調(diào)用alloc給對象分配內(nèi)存時(shí)蔫劣,系統(tǒng)會自動調(diào)用allocWithZone為其分配內(nèi)存歪沃。因此重寫allocWithZone方法就可以保證液走,實(shí)例化對象的時(shí)候嘱根,為對象分配的內(nèi)存永遠(yuǎn)都是同一個(gè)。(ps: alloc與allocWithZone的區(qū)別http://justinyan.me/post/1306)
#import "Person.h"
@interface Person()<NSCopying,NSMutableCopying>
@end
@implementation Person
+(instancetype)sharedInstance
{
return [[self alloc]init];
}
-(instancetype)init
{
if(self = [super init]){
}
return self;
}
//重寫allocWithZone 保證創(chuàng)建對象分配的內(nèi)存是同一個(gè)
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
static Person *p = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
p = [super allocWithZone:zone];
});
return p;
}
上面的代碼就可以保證不管使用的什么初始化方法初始化對象欧引,對象的內(nèi)存地址只有一個(gè)。
既然都寫到這里了维咸,那連copy和mutableCopy也一起處理癌蓖,雖然用到的時(shí)候不多,但不重寫的情況下赞咙,調(diào)用copy會引起crash歹垫。還是那句話排惨,你并不能保證你的隊(duì)友不會做什么碰凶。
-(instancetype)copyWithZone:(NSZone *)zone
{
return [Person sharedInstance];
}
-(instancetype)mutableCopyWithZone:(NSZone *)zone
{
return [Person sharedInstance];
}
創(chuàng)建單例方法二:重寫+(void)initialize和+ (instancetype)alloc
當(dāng)?shù)谝淮握{(diào)用一個(gè)類時(shí),當(dāng)這個(gè)類是第一次被調(diào)用時(shí)面哼,會先調(diào)用+(void)initialize方法闯袒,在調(diào)用初始化方法+ (instancetype)alloc唾那;當(dāng)不是第n>1次時(shí),會直接調(diào)用+ (instancetype)alloc方法。因此可以在initialize方法中初始化單例容达,在alloc做出判斷,若對象已經(jīng)初始化熙揍,就不再次初始化职祷,保證對象只初始化一次。
#import "Person.h"
@interface Person()<NSCopying,NSMutableCopying>
@end
@implementation Person
static Person *_p = nil;
//第一次調(diào)用該類時(shí)調(diào)用
+(void)initialize
{
[self sharedInstance];
}
//初始化對象
+(instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_p = [[self alloc]init];
});
return _p;
}
//重寫alloc 判斷對象是否已經(jīng)初始化届囚。若已經(jīng)初始化有梆,就用原來的;若沒有就c初始化一個(gè)意系。只有第一次調(diào)用該類的時(shí)候泥耀,才會創(chuàng)建。
+(instancetype)alloc
{
if (_p) {
return _p;
}
return [super alloc];
}
-(instancetype)copyWithZone:(NSZone *)zone
{
return [Person sharedInstance];
}
-(instancetype)mutableCopyWithZone:(NSZone *)zone
{
return [Person sharedInstance];
}
單例優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
(1)初始化后可供全局調(diào)用蛔添,方便
(2)只初始化一次痰催,在程序只存在一個(gè)對象,節(jié)省了系統(tǒng)內(nèi)存資源迎瞧。
缺點(diǎn):
(1)不能被繼承
(2)不易被重寫(可以用分類)
(3)在程序運(yùn)行中夸溶,不會被銷毀威酒,會一直占用部分內(nèi)存惰蜜,在閑置時(shí),占用了內(nèi)存資源荧关。
補(bǔ)充:怎么用宏定義 定義單例類咏尝,在類中寫上宏定義后压语,使這個(gè)類變?yōu)閱卫悺?/h4>
在.h中定義宏定義
// .h文件
#define ZYShareSingleton_H(name) \
+(instancetype)shared##name
// .m文件
#define ZYShareSingleton_M(name) \
+(instancetype)shared##name \
{ \
return [[self alloc]init]; \
} \
+(instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static name *instance = nil; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
instance = [super allocWithZone:zone]; \
}); \
return instance; \
} \
-(instancetype)copyWithZone:(NSZone *)zone \
{ \
return [name shared##name]; \
} \
-(instancetype)mutableCopyWithZone:(NSZone *)zone \
{ \
return [name shared##name]; \
}
在.h中定義宏定義
// .h文件
#define ZYShareSingleton_H(name) \
+(instancetype)shared##name
// .m文件
#define ZYShareSingleton_M(name) \
+(instancetype)shared##name \
{ \
return [[self alloc]init]; \
} \
+(instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static name *instance = nil; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
instance = [super allocWithZone:zone]; \
}); \
return instance; \
} \
-(instancetype)copyWithZone:(NSZone *)zone \
{ \
return [name shared##name]; \
} \
-(instancetype)mutableCopyWithZone:(NSZone *)zone \
{ \
return [name shared##name]; \
}
這樣,在需要使用單例的類Person中如下定義:
在類的.h中 寫上ZYShareSingleton_H(Person);
@interface Person : NSObject
ZYShareSingleton_H(Person);
@end
在類的.m中 寫上ZYShareSingleton_M(Person);
@implementation Person
ZYShareSingleton_M(Person);
@end
demo地址:https://github.com/coloredSky/iOS.git
小知識:宏定義中的字符用處
(1)
字符:##
作用:鏈接字符創(chuàng)
例:
#define KName(n) x ## n
=>使用
KName(8)
=>輸出
x8
(2)
字符:\
作用:續(xù)行符,當(dāng)定義的宏不能用一行表達(dá)完整時(shí)编检,用 \ 表示下一行繼續(xù)此宏的定義胎食。
例:
#define K_Man(a,b) \
a>=b?a:b
=>使用
K_Man(2,3)
=>輸出
3
(3)
字符:#
作用:字符串化。給參數(shù)加雙引號允懂。
例:
#define K_Char(n) #n
=>使用
K_Char(888)
=>輸出
"888"
(4)
字符:VA_ARGS
作用:用來接受不定數(shù)量的參數(shù)厕怜。可以是一個(gè)蕾总、兩個(gè)粥航、n個(gè)參數(shù)
例:
#define K_Log(...) NSLog(__VA_ARGS__)
=>使用
K_Log(@"%@%@",@"111",@"222");
=>輸出
111222
(5) ## 的特殊用法
"##"放在逗號","和參數(shù)之間,如果參數(shù)留空的話生百,那么"##"前面的","就會被刪掉递雀,從而防止編譯錯(cuò)誤。
例:
#define K_Log(parameter1, ...) NSLog(parameter1, __VA_ARGS__)
K_Log(@"11111");//會報(bào)錯(cuò)蚀浆,因?yàn)橹辽傩枰獋鬟f兩個(gè)參數(shù)缀程,而只傳了一個(gè)
#define K_Log(parameter1, ...) NSLog(parameter1, ##__VA_ARGS__)
K_Log(@"11111");//不會報(bào)錯(cuò)搜吧,雖然只傳了一個(gè)參數(shù),但##將前面的","去除了