2013-07-23 43 views
8

tôi thấy chủ đề an toàn phiên bảnLàm thế nào để thực hiện đúng cách tương thích ARC và `cấp phát init` an toàn Singleton lớp học?

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

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

nhưng những gì sẽ xảy ra nếu ai đó chỉ gọi [MyClass alloc] init]? Làm thế nào để làm cho nó trở lại cùng một ví dụ như phương pháp +(MyClass *)singleton?

Trả lời

12

Apple khuyến cáo việc thực hiện nghiêm ngặt singleton (không có đối tượng sống khác cùng loại được phép) theo cách này:

+ (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; 
} 

Link to the apple documentation (bottom of page, Without ARC)

+0

bạn đã làm một dispatch_once(), có cần phải làm gì nếu (! SingletonInstance)? Tôi tin rằng dispatch_once() sẽ xử lý điều đó cho chúng tôi trên những thứ khác như đồng bộ hóa. – pnizzle

+0

@pnizzle bạn đúng, dispatch_once sẽ là đủ, kiểm tra các ví dụ trước khi có thể được nhanh hơn mặc dù. –

+0

Có phải 'allocWithZone:' không được chấp nhận? Có vẻ như khi tôi cố gắng thực hiện nó. – Fogh

0

Tôi nhặt mẫu mã này từ blog duckrowing: http://www.duckrowing.com/2011/11/09/using-the-singleton-pattern-in-objective-c-part-2/

Trong .h chúng tôi có

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

và trong .m chúng tôi có

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 

Thông báo các allocWithZone .

+0

Đầu tiên, hành vi mà bạn đang cố triển khai là [[alloc] init] phải trả lại chính xác cùng một đối tượng như + sharedFoo. Có những trường hợp như NSFileManager trong đó có một singleton được chia sẻ, nhưng [[alloc] init] trả về một cá thể khác. Trong trường hợp của bạn, tôi nghĩ rằng bạn có thể chỉ cần đặt dispatch_once vào init thay vì dispatch_sync, và quên về allocWithZone. – gnasher729

+0

làm thế nào bạn có thể có một singleton với nhiều hơn một trường hợp duy nhất? –

+0

NSFileManager cung cấp một _singleton_ có thể thực hiện hầu hết những việc bạn muốn làm. Nó cũng cung cấp các cá thể cá thể mà bạn có thể sửa đổi cho các tình huống phức tạp hơn. Vì vậy, sharedInstance là một singleton, nhưng [[alloc] init] thì không. – gnasher729

2

này có thể hữu ích,

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