什麼是單例模式?html
單例模式是一個類在系統中只有一個實例對象。經過全局的一個入口點對這個實例對象進行訪問。在iOS開發中,單例模式是很是有用的一種設計模式。以下圖,是一個簡單的例模式的UML類圖。ios
iOS SDK中也有許多類使用了單例模式,例如,UIApplication:當程序啓動的時候,會調用UIApplicationMain方法,在該方法中,會實例化一個UIApplication對象,以後在程序中的任意地方調用sharedApplication方法都將返回一個與當前應用程序相關的UIApplication實例(UIApplicationMain方法中建立的UIApplication單例)。設計模式
何時使用單例模式?安全
在程序中,單例模式常常用於只但願一個類只有一個實例,而不運行一個類還有兩個以上的實例。固然,在iOS SDK中,根據特定的需求,有些類不只提供了單例訪問的接口,還爲開發者提供了實例化一個新的對象接口,例如,NSFileManager能夠經過defaultManager方法返回相同的一個NSFileManager對象。若是須要新的一個NSFileManager實例對象,能夠經過init方法。app
iOS中單例模式的實現ui
iOS中單例模式的實現方式通常分爲兩種:Non-ARC(非ARC)和ARC+GCD。atom
1.Non-ARC(非ARC)
非ARC的實現方法以下所示:spa
BVNonARCSingleton.h
1.//
2.// BVNonARCSingleton.h
3.// SingletonPattern
4.//
5.// Created by BeyondVincent on 13-5-9.
6.// Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8.
9.#import <Foundation/Foundation.h>
10.
11.@interface BVNonARCSingleton : NSObject
12.
13.@property ( nonatomic, retain) NSString *tempProperty;
14.+ (BVNonARCSingleton *)sharedInstance;
15.
16.@end11.@implementation BVNonARCSingleton
12.
13.static BVNonARCSingleton *sharedInstance = nil;
14.
15.// 獲取一個sharedInstance實例,若是有必要的話,實例化一個
16.+ (BVNonARCSingleton *)sharedInstance {
17. if (sharedInstance == nil) {
18. sharedInstance = [[super allocWithZone:NULL] init];
19. }
20.
21. return sharedInstance;
22.}
23.
24.// 當第一次使用這個單例時,會調用這個init方法。
25.- (id)init
26.{
27. self = [super init];
28.
29. if (self) {
30. // 一般在這裏作一些相關的初始化任務
31. }
32.
33. return self;
34.}
35.
36.// 這個dealloc方法永遠都不會被調用--由於在程序的生命週期內容,該單例一直都存在。(因此該方法能夠不用實現)
37.-(void)dealloc
38.{
39. [super dealloc];
40.}
41.
42.// 經過返回當前的sharedInstance實例,就能防止實例化一個新的對象。
43.+ (id)allocWithZone:(NSZone*)zone {
44. return [[self sharedInstance] retain];
45.}
46.
47.// 一樣,不但願生成單例的多個拷貝。
48.- (id)copyWithZone:(NSZone *)zone {
49. return self;
50.}
51.
52.// 什麼也不作——該單例並不須要一個引用計數(retain counter)
53.- (id)retain {
54. return self;
55.}
56.
57.// 替換掉引用計數——這樣就永遠都不會release這個單例。
58.- (NSUInteger)retainCount {
59. return NSUIntegerMax;
60.}
61.
62.// 該方法是空的——不但願用戶release掉這個對象。
63.- (oneway void)release {
64.
65.}
66.
67.//除了返回單例外,什麼也不作。
68.- (id)autorelease {
69. return self;
70.}
71.
72.@end2.@synchronized (self)
3.{
4. if(sharedInstance == nil)
5. {
6. sharedInstance = [[super allocWithZone:NULL] init];
7. }
8.}11.@interface BVARCSingleton : NSObject
12.
13.@property ( nonatomic, weak) NSString *tempProperty;
14.+ (BVARCSingleton *)sharedInstance;
15.
16.@end11.@implementation BVARCSingleton
12.
13.+ (BVARCSingleton *) sharedInstance
14.{
15. static BVARCSingleton *sharedInstance = nil ;
16. static dispatch_once_t onceToken; // 鎖
17. dispatch_once (& onceToken, ^ { // 最多調用一次
18. sharedInstance = [[self alloc] init];
19. });
20. return sharedInstance;
21.}
22.
23.// 當第一次使用這個單例時,會調用這個init方法。
24.- (id)init
25.{
26. self = [super init];
27.
28. if (self) {
29. // 一般在這裏作一些相關的初始化任務
30. }
31.
32. return self;
33.}
34.
35.@end2.@synchronized (self)
.net
BVNonARCSingleton.m
1.//
2.// BVNonARCSingleton.m
3.// SingletonPattern
4.//
5.// Created by BeyondVincent on 13-5-9.
6.// Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8.
9.#import "BVNonARCSingleton.h"
10.
實際上上面的代碼蘋果官網也有提供:Creating a Singleton Instance,只不過沒有給出頭文件的定義。上面用非ARC實現單例的方法是線程不安全的,若是有多個線程同時調用sharedInstance方法獲取一個實例,而sharedInstance方法須要花費1-2秒鐘的時間,那麼BVNonARCSingleton的init方法就可能會被屢次調用,也就是不一樣線程得到的BVNonARCSingleton有可能不是同一個實例。怎麼解決線程的不安全呢?答案是使用@synchronized來建立互斥鎖便可。線程
1.// 保證在實例化的時候是線程安全的(固然,該方法不能保證該單例中全部方法的調用都是線程安全的)
2.@synchronized (self)
3.{
4. if(sharedInstance == nil)
5. {
6. sharedInstance = [[super allocWithZone:NULL] init];
7. }
8.}
經過上面的代碼就能保存線程安全。
1.<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">提醒:在iOS中,通常不建議使用非ARC來實現單例模式。更好的方法是使用ARC+GCD來實現。</span>
2.ARC+GCD
經過ARC+GCD的方法來實現單例模式的很是簡單的。下面先來看看具體實現:
BVARCSingleton.h
1.//
2.// BVARCSingleton.h
3.// SingletonPattern
4.//
5.// Created by BeyondVincent on 13-5-9.
6.// Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8.
9.#import <Foundation/Foundation.h>
10.
BVARCSingleton.m
1.//
2.// BVARCSingleton.m
3.// SingletonPattern
4.//
5.// Created by BeyondVincent on 13-5-9.
6.// Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8.
9.#import "BVARCSingleton.h"
10.
在上面的代碼中,調用Grand Central Dispatch (GCD)中的dispatch_once方法就能夠確保BVARCSingleton只被實例化一次。而且該方法是線程安全的,咱們不用擔憂在不一樣的線程中,會得到不一樣的實例。(固然,該方法一樣不能保證該單例中全部方法的調用都是線程安全的)。
固然,在ARC中,不用GCD也是能夠作到線程安全的,跟以前非ARC代碼中使用@synchronized同樣,以下代碼:
1. // 不使用GCD,經過@synchronized
2.@synchronized (self)
3.{
4. if(sharedInstance == nil)
5. {
6. sharedInstance = [[self alloc] init];
7. }
8.}
爲了簡化使用ARC+GCD來建立單例,能夠定義下面這樣的一個宏:
1.#define DEFINE_SHARED_INSTANCE_USING_BLOCK(block) \
2.static dispatch_once_t onceToken = 0; \
3.__strong static id sharedInstance = nil; \
4.dispatch_once(&onceToken, ^{ \
5.sharedInstance = block(); \
6.}); \
7.return sharedInstance; \
實例化的實現方法以下所示:
1.+ (BVARCSingleton *) sharedInstance
2.{
3. DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
4. return [[self alloc] init];
5. });
6.}
單例的使用
單例的使用方法很簡單,在代碼中的任意位置,以下使用便可:
在BVAppDelegate.m中添加頭文件:
1.#import "BVNonARCSingleton.h"
2.#import "BVARCSingleton.h"
以下使用方法:
1.- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2.{
3. [BVNonARCSingleton sharedInstance].tempProperty = @"非ARC單例的實現";
4. NSLog(@"%@", [BVNonARCSingleton sharedInstance].tempProperty);
5.
6. [BVARCSingleton sharedInstance].tempProperty = @"ARC單例的實現";
7. NSLog(@"%@", [BVARCSingleton sharedInstance].tempProperty);
8.
9. return YES;
10.}
運行程序,會在控制檯窗口輸出以下內容:
1.2013-05-09 16:44:07.649 SingletonPattern[5159:c07] 非ARC單例的實現2.2013-05-09 16:44:33.204 SingletonPattern[5159:c07] ARC單例的實現