Objective-C ARC單例模式

 

什麼是單例模式?

單例模式是一個類在系統中只有一個實例對象。通過全局的一個入口點對這個實例對象進行訪問。在iOS開發中,單例模式是非常有用的一種設計模式。如下圖,是一個簡單的例模式的UML類圖。

 

iOS SDK中也有許多類使用了單例模式,例如,UIApplication:當程序啓動的時候,會調用UIApplicationMain方法,在該方法中,會實例化一個UIApplication對象,之後在程序中的任意地方調用sharedApplication方法都將返回一個與當前應用程序相關的UIApplication實例(UIApplicationMain方法中創建的UIApplication單例)。

 

什麼時候使用單例模式?

在程序中,單例模式經常用於只希望一個類只有一個實例,而不運行一個類還有兩個以上的實例。當然,在iOS SDK中,根據特定的需求,有些類不僅提供了單例訪問的接口,還爲開發者提供了實例化一個新的對象接口,例如,NSFileManager可以通過defaultManager方法返回相同的一個NSFileManager對象。如果需要新的一個NSFileManager實例對象,可以通過init方法。

 

iOS中單例模式的實現

iOS中單例模式的實現方式一般分爲兩種:Non-ARC(非ARC)和ARC+GCD。


1.Non-ARC(非ARC)
非ARC的實現方法如下所示:

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)


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.提醒:在iOS中,一般不建議使用非ARC來實現單例模式。更好的方法是使用ARC+GCD來實現。

 

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.}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章