iOS設計模式(02):單例模式


 

什麼是單例模式?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 &lt;Foundation/Foundation.h&gt;
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(&amp;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單例的實現

相關文章
相關標籤/搜索