2013-07-23 65 views
8

我看到線程安全的版本如何正確實現ARC兼容和`alloc init`安全的Singleton類?

+(MyClass *)singleton { 
    static dispatch_once_t pred; 
    static MyClass *shared = nil; 

    dispatch_once(&pred, ^{ 
     shared = [[MyClass alloc] init]; 
    }); 
    return shared; 
} 

,但如果有人只是調用[MyClass alloc] init]會發生什麼?如何使它返回與+(MyClass *)singleton方法相同的實例?

回答

12

蘋果建議嚴格單執行(同一類型的其他活對象是允許的)這種方式:

+ (instancetype)singleton { 
    static id singletonInstance = nil; 
    if (!singletonInstance) { 
     static dispatch_once_t onceToken; 
     dispatch_once(&onceToken, ^{ 
      singletonInstance = [[super allocWithZone:NULL] init]; 
     }); 
    } 
    return singletonInstance; 
} 

+ (id)allocWithZone:(NSZone *)zone { 
    return [self singleton]; 
} 

- (id)copyWithZone:(NSZone *)zone { 
    return self; 
} 

鏈接到蘋果documentation (bottom of page, Without ARC)

+0

你已經在做一個dispatch_once(),是否需要做if(!singletonInstance)?我相信dispatch_once()會爲我們處理其他事情,比如同步。 – pnizzle

+0

@pnizzle你是對的,dispatch_once就足夠了,但檢查實例之前可能會更快。 –

+0

'allocWithZone:'已棄用?看起來像這樣,當我嘗試實施它。 – Fogh

0

我從duckrowing博客拿起這個代碼示例:http://www.duckrowing.com/2011/11/09/using-the-singleton-pattern-in-objective-c-part-2/

在.H我們

@interface Foo : NSObject 
+ (Foo *) sharedFoo; 
@end 

,並在.M我們

static SLFoo *sharedInstance = nil; 
static dispatch_queue_t serialQueue; 

@implementation Foo 

- (id)init 
{ 
    id __block obj; 

    dispatch_sync(serialQueue, ^{ 
     obj = [super init]; 
     if (obj) { 
      ; 
     } 
    }); 

    self = obj; 
    return self; 
} 

+ (Foo *) sharedFoo; 
{ 
    static dispatch_once_t onceQueue; 

    dispatch_once(&onceQueue, ^{ 
     if (sharedInstance) { 
      return; 
     } 
     sharedInstance = [[Foo alloc]init]; 
    }); 
    return sharedInstance; 

} 

+ (id)allocWithZone:(NSZone *)zone 
{ 
    static dispatch_once_t onceQueue; 

    dispatch_once(&onceQueue, ^{ 
     serialQueue = dispatch_queue_create("com.mydomain.myApp.SerialQueueFoo", NULL); 
     if (sharedInstance == nil) { 
      sharedInstance = [super allocWithZone:zone]; 
     } 
    }); 

    return sharedInstance; 
} 

@end 

通知的allocWithZone 。

+0

首先,您試圖實現的行爲是[[alloc] init]應該返回與+ sharedFoo完全相同的對象。有些情況下像NSFileManager那裏有一個共享的單例,但[[alloc] init]返回一個不同的實例。在你的情況下,我認爲你可以將一個dispatch_once放入init,而不是dispatch_sync,並忘記allocWithZone。 – gnasher729

+0

你怎麼能有一個以上的獨特實例的單身人士? –

+0

NSFileManager提供了一個_singleton_,可以完成大部分你想要做的事情。它還提供您可以修改更復雜情況的個別實例。所以sharedInstance是一個單例,但[[alloc] init]不是。 – gnasher729

2

這可能是有幫助的,

static Foo *sharedInstance = nil; 

+ (Foo *)sharedInstance { 

    if (sharedInstance == nil) { 

     sharedInstance = [[super allocWithZone:NULL] init]; 
    } 

    return sharedInstance; 
} 

+ (id)allocWithZone:(NSZone *)zone { 
    @synchronized(self) 
    { 
     if (sharedInstance == nil) { 
      sharedInstance = [super allocWithZone:zone]; 
      return sharedInstance; 
     } 
    } 
    return nil; 
}