單例

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]; \
}

這樣,在需要使用單例的類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ù),但##將前面的","去除了
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末杨凑,一起剝皮案震驚了整個(gè)濱河市滤奈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撩满,老刑警劉巖蜒程,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鹦牛,居然都是意外死亡搞糕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門曼追,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窍仰,“玉大人,你說我怎么就攤上這事礼殊【运保” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵晶伦,是天一觀的道長碟狞。 經(jīng)常有香客問我,道長婚陪,這世上最難降的妖魔是什么族沃? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮泌参,結(jié)果婚禮上脆淹,老公的妹妹穿的比我還像新娘。我一直安慰自己沽一,他們只是感情好盖溺,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铣缠,像睡著了一般烘嘱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝗蛙,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天蝇庭,我揣著相機(jī)與錄音,去河邊找鬼捡硅。 笑死遗契,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的病曾。 我是一名探鬼主播牍蜂,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泰涂!你這毒婦竟也來了鲫竞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤逼蒙,失蹤者是張志新(化名)和其女友劉穎从绘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體是牢,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡僵井,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驳棱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片批什。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖社搅,靈堂內(nèi)的尸體忽然破棺而出驻债,到底是詐尸還是另有隱情,我是刑警寧澤形葬,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布合呐,位于F島的核電站,受9級特大地震影響笙以,放射性物質(zhì)發(fā)生泄漏淌实。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一猖腕、第九天 我趴在偏房一處隱蔽的房頂上張望拆祈。 院中可真熱鬧,春花似錦谈息、人聲如沸缘屹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轻姿。三九已至,卻和暖如春逻炊,著一層夾襖步出監(jiān)牢的瞬間互亮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工余素, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豹休,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓桨吊,卻偏偏與公主長得像威根,于是被迫代替她去往敵國和親凤巨。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容