Objective-c單例模式詳解

單例模式出現(xiàn)以后,關(guān)于它的爭(zhēng)執(zhí)就一直存在。在開發(fā)項(xiàng)目中,有很多時(shí)候我們需要一個(gè)全局的對(duì)象,而且要保證全局有且僅有一份即可。沒錯(cuò),單例在這個(gè)時(shí)候就是最佳的選擇,但是需要注意的是:在多線程的環(huán)境下也需要做好線程保護(hù)。其實(shí)系統(tǒng)已經(jīng)有很多單例存在,例如UIApplication、NSNotification、NSFileManager等等就是很不錯(cuò)的例子——我們總有時(shí)候需要用到單例模式,不過寫起代碼來還是需要考量考量。

  1. 我們先來看一個(gè)最簡(jiǎn)單的單例,假設(shè)我們有一個(gè)testClass的類需要實(shí)現(xiàn)單例:

     + (id)sharedInstance {  
         static testClass *sharedInstance = nil;  
         if (!sharedInstance) {  
             sharedInstance = [[self alloc] init];  
         }  
         return sharedInstance;  
     }  
    
  2. 熟悉單例的童鞋一眼就能看出,這里根本沒有考慮線程安全的問題,需要加上線程鎖。

     + (id)sharedInstance {  
         static testClass *sharedInstance = nil;  
         @synchronized(self) {  
             if (!sharedInstance) {  
                 sharedInstance = [[self alloc] init];  
             }  
         }  
         return sharedInstance;  
     }  
    
  3. 這是很常見的寫法。不過,在GCD推出后,有個(gè)dispatch_once方法,可以使單例的實(shí)現(xiàn)更加容易,dispatch_once的函數(shù)原型如下:

     void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);  
    

    我們可以看到這個(gè)函數(shù)接收一個(gè)dispatch_once_t的參數(shù),還有一個(gè)塊參數(shù)。對(duì)于一個(gè)給定的predicate 來說,該函數(shù)會(huì)保證相關(guān)的塊必定會(huì)執(zhí)行,而且只執(zhí)行一次,最重要的是——這個(gè)方法是完全線程安全的。需要 注意的是,對(duì)于只需要執(zhí)行一次的塊來說,傳入的predicate必須是完全相同的,所以predicate常常會(huì)用 static或者global來修飾。

     + (id)sharedInstance {  
         static testClass *sharedInstance = nil;  
         static dispatch_once_t once;  
         dispatch_once(&once, ^{  
             sharedInstance = [[self alloc] init];  
         });  
         return sharedInstance;  
     }  
    
  4. 我們知道,創(chuàng)建對(duì)象的步驟分為申請(qǐng)內(nèi)存(alloc)、初始化(init)這兩個(gè)步驟,我們要確保對(duì)象的唯一性,因此在第一步這個(gè)階段我們就要攔截它。當(dāng)我們調(diào)用alloc方法時(shí),OC內(nèi)部會(huì)調(diào)用allocWithZone這個(gè)方法來申請(qǐng)內(nèi)存,我們重寫這個(gè)方法,然后在這個(gè)方法中調(diào)用shareInstance方法返回單例對(duì)象,這樣就可以達(dá)到我們的目的??截悓?duì)象也是同樣的原理,重寫copyWithZone方法,然后在這個(gè)方法中調(diào)用shareInstance方法返回單例對(duì)象。

下面來看看兩組例子:

一般寫法:

#import <Foundation/Foundation.h>

@interface SingleClass : NSObject

+(instancetype) shareInstance ;

@end
 
 
#import "SingleClass.h"

@implementation SingleClass

static SingleClass *_sharedInstance = nil;

+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[self alloc] init] ;
    }) ;
    
    return _sharedInstance ;
}

@end

具體使用,ViewController:

#import "ViewController.h"
#import "SingleClass.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    NSLog(@"開始《《《");
    SingleClass *obj1 = [SingleClass shareInstance] ;
    NSLog(@"obj1 = %@.", obj1) ;
    
    SingleClass *obj2 = [SingleClass shareInstance] ;
    NSLog(@"obj2 = %@.", obj2) ;
    
    SingleClass *obj3 = [[SingleClass alloc] init] ;
    NSLog(@"obj3 = %@.", obj3) ;
    
    NSLog(@"結(jié)束》》》");
}

@end

輸出結(jié)果為 :

2016-04-11 15:49:29.494 aotulayoutDemo[7267:202275] 開始《《《
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj1 = <SingleClass: 0x7f901142d660>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj2 = <SingleClass: 0x7f901142d660>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj3 = <SingleClass: 0x7f901160e3a0>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] 結(jié)束》》》

在這里可以看到,當(dāng)我們調(diào)用shareInstance方法時(shí)獲取到的對(duì)象是相同的,但是當(dāng)我們通過alloc和init來構(gòu)造對(duì)象的時(shí)候,得到的對(duì)象卻是不一樣的。我們通過不同的途徑得到不同的對(duì)象,顯然是不行的。我們必須要確保對(duì)象的唯一性,所以我們就需要封鎖用戶通過alloc和init以及copy來構(gòu)造對(duì)象這條道路。

下面來看看嚴(yán)謹(jǐn)?shù)膶懛ǎ?/p>

#import "Singleton.h"
 
@implementation Singleton
 
static Singleton* _instance = nil;
 
+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init] ;
    }) ;
     
    return _instance ;
}
 
+(id) allocWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}
 
-(id) copyWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}
 
@end

再看看效果如何,ViewController:

#import "ViewController.h"
#import "SingleClass.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    NSLog(@"開始《《《");
    SingleClass *obj1 = [SingleClass shareInstance] ;
    NSLog(@"obj1 = %@.", obj1) ;
    
    SingleClass *obj2 = [SingleClass shareInstance] ;
    NSLog(@"obj2 = %@.", obj2) ;
    
    SingleClass *obj3 = [[SingleClass alloc] init] ;
    NSLog(@"obj3 = %@.", obj3) ;
    
    SingleClass* obj4 = [[SingleClass alloc] init] ;
    NSLog(@"obj4 = %@.", [obj4 copy]) ;

    NSLog(@"結(jié)束》》》");
}

@end

輸出結(jié)果:

2016-04-11 15:56:27.261 aotulayoutDemo[7373:205889] 開始《《《
2016-04-11 15:56:27.263 aotulayoutDemo[7373:205889] obj1 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.263 aotulayoutDemo[7373:205889] obj2 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] obj3 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] obj4 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] 結(jié)束》》》

這里我們可以看到,獲取到的對(duì)象都是一樣的了。

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

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

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