iOS單例詳解

在開發(fā)中經(jīng)常會用到單例設計模式,目的就是為了在程序的整個生命周期內(nèi),只會創(chuàng)建一個類的實例對象,而且只要程序不被殺死,該實例對象就不會被釋放。下面我們來看看單例的概念、用途、如何創(chuàng)建,以便加深理解。

作用

* 在應用這個模式時,單例對象的類必須保證只有一個實例存在。許多時候整個系統(tǒng)只需要擁有一個的全局對象,這樣有利于我們協(xié)調(diào)系統(tǒng)整體的行為。比如在APP開發(fā)中我們可能在任何地方都要使用用戶的信息,那么可以在登錄的時候就把用戶信息存放在一個文件里面,這些配置數(shù)據(jù)由一個單例對象統(tǒng)一讀取,然后服務進程中的其他對象再通過這個單例對象獲取這些配置信息。這種方式簡化了在復雜環(huán)境下的配置管理。

* 有的情況下,某個類可能只能有一個實例。比如說你寫了一個類用來播放音樂,那么不管任何時候只能有一個該類的實例來播放聲音。再比如,一臺計算機上可以連好幾個打印機,但是這個計算機上的打印程序只能有一個,這里就可以通過單例模式來避免兩個打印任務同時輸出到打印機中,即在整個的打印過程中我只有一個打印程序的實例。

創(chuàng)建單例

有兩種方法來創(chuàng)建單例,下面分別介紹

1、GCD方式創(chuàng)建單例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24 static?id?_instance;

+?(instancetype)allocWithZone:(struct?_NSZone?*)zone

{

static?dispatch_once_t?onceToken;

dispatch_once(&onceToken,?^{

_instance?=?[super?allocWithZone:zone];

});

return?_instance;

}

+?(instancetype)sharedInstance

{

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;

}

2、互斥鎖方式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23 static?id?_instance;

+?(instancetype)allocWithZone:(struct?_NSZone?*)zone

{

@synchronized(self)?{

if?(_instance?==?nil)?{

_instance?=?[super?allocWithZone:zone];

}

}

return?_instance;

}

+?(instancetype)sharedInstance

{

@synchronized(self)?{

if?(_instance?==?nil)?{

_instance?=?[[self?alloc]?init];

}

}

return?_instance;

}

-?(id)copyWithZone:(NSZone?*)zone

{

return?_instance;

}

上面兩種方式都可以創(chuàng)建單例,而且保證了用戶不管是通過shareInstance方法,還是alloc、copy方法得到的實例都是一樣的。

上面代碼的關鍵之處就在于如何在多線程情況下保證創(chuàng)建的單例還是同一個。

我們先看看在GCD情況下,如果不使用dispatch_once和同步鎖創(chuàng)建單例會出現(xiàn)什么問題,去掉兩者后創(chuàng)建單例的代碼如下

1

2

3

4

5

6 +?(instancetype)sharedInstance

{

if?(_instance?==?nil)?{

_instance?=?[[self?alloc]?init];

}

}

假設此時有兩條線程:線程1和線程2,都在調(diào)用shareInstance方法來創(chuàng)建單例,那么線程1運行到if (_instance == nil)出發(fā)現(xiàn)_instance = nil,那么就會初始化一個_instance,假設此時線程2也運行到if的判斷處了,此時線程1還沒有創(chuàng)建完成實例_instance,所以此時_instance = nil還是成立的,那么線程2又會創(chuàng)建一個_instace。

此時就創(chuàng)建了兩個實例對象,導致問題。

解決辦法1、使用dispatch_once

dispatch_once保證程序在運行過程中只會被運行一次,那么假設此時線程1先執(zhí)行shareInstance方法,創(chuàng)建了一個實例對象,線程2就不會再去執(zhí)行dispatch_once的代碼了。從而保證了只會創(chuàng)建一個實例對象。

解決辦法2、使用互斥鎖

假設此時線程1在執(zhí)行shareInstance方法,那么synchronize大括號內(nèi)創(chuàng)建單例的代碼,如下所示:

1

2

3 if?(_instance?==?nil)?{

_instance?=?[[self?alloc]?init];

}

就會被當做一個任務被加上了一把鎖。此時假設線程2也想執(zhí)行shareInstance方法創(chuàng)建單例,但是看到了線程1加的互斥鎖,就會進入睡眠模式。等到線程1執(zhí)行完畢,才會被喚醒,然后去執(zhí)行上面所示的創(chuàng)建單例的代碼,但是此時_instance !=nil,所以不會再創(chuàng)建新的實例對象了。從而保證只會創(chuàng)建一個實例對象。

但是互斥鎖會影響性能,所以最好還是使用GCD方式創(chuàng)建單例。

宏創(chuàng)建單例

如果我們需要在程序中創(chuàng)建多個單例,那么需要在每個類中都寫上一次上述代碼,非常繁瑣。

我們可以使用宏來封裝單例的創(chuàng)建,這樣任何類需要創(chuàng)建單例,只需要一行代碼就搞定了。

實現(xiàn)代碼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32 Singleton.h文件

==================================

#define?SingletonH(name)?+?(instancetype)shared##name;

#define?SingletonM(name)?\

static?id?_instance;?\

\

+?(instancetype)allocWithZone:(struct?_NSZone?*)zone?\

{?\

static?dispatch_once_t?onceToken;?\

dispatch_once(&onceToken,?^{?\

_instance?=?[super?allocWithZone:zone];?\

});?\

return?_instance;?\

}?\

\

+?(instancetype)shared##name?\

{?\

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;?\

}

如何調(diào)用

假設我們要在類viewcontroller中使用,調(diào)用方法如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18 viewcontroller.h文件

===========================

#import

#import?"Singleton.h"

@interface?ViewController?:?UIViewController

SingletonH(viewController)

@end

viewcontroller.m文件

===========================

@interface?ViewController?()

@end

@implementation?ViewController

SingletonM(ViewController)

-?(void)viewDidLoad?{

[super?viewDidLoad];

NSLog(@"%@?%@?%@?%@",?[ViewController?sharedViewController],[ViewController?sharedViewController],?[[ViewController?alloc]?init],[[ViewController?alloc]?init]);

}

@end

輸出結果

可以看到四個對象的內(nèi)存地址完全一樣,說明是同一個對象。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 在開發(fā)中經(jīng)常會用到單例設計模式,目的就是為了在程序的整個生命周期內(nèi),只會創(chuàng)建一個類的實例對象,而且只要程序不被殺死...
    VincentHK閱讀 729評論 0 3
  • 在開發(fā)中經(jīng)常會用到單例設計模式,目的就是為了在程序的整個生命周期內(nèi),只會創(chuàng)建一個類的實例對象,而且只要程序不被殺死...
    不要重名就好閱讀 596評論 0 0
  • 進程和線程 首先,在了解多線程之前要了解什么是進程,什么是線程 什么是進程呢?進程是指在系統(tǒng)中正在運行的一個應用程...
    擱淺的青蛙閱讀 459評論 0 0
  • iOS開發(fā)—單例模式 一、簡單說明: 設計模式:多年軟件開發(fā),總結出來的一套經(jīng)驗、方法和工具 二、單例模式說明 (...
    牛仔褲小毛驢閱讀 737評論 0 0
  • 結婚前,總以為日子是歲月靜好。 結婚后,才發(fā)現(xiàn)生活是兵荒馬亂。 1 一日下午四點多,我一邊電話采訪專家,一邊鍵盤敲...
    墨花閱讀 930評論 1 8

友情鏈接更多精彩內(nèi)容