Create singleton using GCD's dispatch_once in Objective C
如果您可以瞄准iOS 4.0或更高版本
使用GCD,这是在目标C(线程安全)中创建单例的最佳方法吗?
1 2 3 4 5 6 7 8 9 | + (instancetype)sharedInstance { static dispatch_once_t once; static id sharedInstance; dispatch_once(&once, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; } |
这是创建类实例的一种完全可接受且线程安全的方法。从技术上讲,它可能不是"单例"(因为这些对象中只能有1个),但只要您只使用
实例化
知道它,爱它。
把它作为一个例子,说明如何关注低层次的细节,可以让你洞察到转换Objective-C的强大的新方法。
请参阅此处:InstanceType
1 2 3 4 5 6 7 8 9 10 11 | + (instancetype)sharedInstance { static dispatch_once_t once; static id sharedInstance; dispatch_once(&once, ^ { sharedInstance = [self new]; }); return sharedInstance; } |
1 2 3 4 5 6 7 8 9 10 11 | + (Class*)sharedInstance { static dispatch_once_t once; static Class *sharedInstance; dispatch_once(&once, ^ { sharedInstance = [self new]; }); return sharedInstance; } |
米辛尔顿
1 2 3 4 5 6 7 8 9 10 | @interface MySingleton : NSObject +(instancetype)sharedInstance; +(instancetype)alloc __attribute__((unavailable("alloc not available, call sharedInstance instead"))); -(instancetype)init __attribute__((unavailable("init not available, call sharedInstance instead"))); +(instancetype)new __attribute__((unavailable("new not available, call sharedInstance instead"))); -(instancetype)copy __attribute__((unavailable("copy not available, call sharedInstance instead"))); @end |
米辛尔顿
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @implementation MySingleton +(instancetype)sharedInstance { static dispatch_once_t pred; static id shared = nil; dispatch_once(&pred, ^{ shared = [[super alloc] initUniqueInstance]; }); return shared; } -(instancetype)initUniqueInstance { return [super init]; } @end |
您可以避免通过重写alloc方法来分配类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | @implementation MyClass static BOOL useinside = NO; static id _sharedObject = nil; +(id) alloc { if (!useinside) { @throw [NSException exceptionWithName:@"Singleton Vialotaion" reason:@"You are violating the singleton class usage. Please call +sharedInstance method" userInfo:nil]; } else { return [super alloc]; } } +(id)sharedInstance { static dispatch_once_t p = 0; dispatch_once(&p, ^{ useinside = YES; _sharedObject = [[MyClass alloc] init]; useinside = NO; }); // returns the same object each time return _sharedObject; } |
戴夫是对的,那很好。您可能希望查看Apple关于创建singleton的文档,了解有关实现其他一些方法的提示,以确保如果类选择不使用sharedfoo方法,则只能创建一个方法。
如果要确保[[myclass alloc]in it]返回与sharedinstance相同的对象(在我看来,这不是必需的,但有些人希望如此),则可以非常轻松、安全地使用第二个分派u一次:
1 2 3 4 5 6 7 8 9 10 11 12 13 | - (instancetype)init { static dispatch_once_t once; static Class *sharedInstance; dispatch_once(&once, ^ { // Your normal init code goes here. sharedInstance = self; }); return sharedInstance; } |
这允许[[MyClass alloc]init]和[MyClass sharedInstance]的任何组合返回相同的对象;[MyClass sharedInstance]只会更有效率。工作原理:【myclass sharedinstance】会调用一次[[myclass alloc]init]。其他代码也可以任意多次调用它。init的第一个调用程序将进行"正常"初始化,并将singleton对象存储在init方法中。稍后对init的任何调用都将完全忽略alloc返回的内容并返回相同的sharedInstance;alloc的结果将被释放。
+sharedInstance方法将一如既往地工作。如果它不是第一个调用[[myclass alloc]init]的调用方,那么init的结果不是alloc调用的结果,但这是可以的。
您会问这是否是"创建singleton的最佳方法"。
一些想法:
首先,是的,这是一个线程安全的解决方案。这种
但你问,这是否是最好的方法。不过,我们应该承认,当与单体结合使用时,
但这种方法中的
1 2 | ImageCache *imageCache = [ImageCache sharedCache]; // fine BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!! |
为了实现这一点,您必须确保子类实现它们自己的
总之,您的原始
为了与swift实现最佳互操作性,您可能希望将其定义为属性,而不是类方法,例如:
1 2 3 | @interface Foo : NSObject @property (class, readonly, strong) Foo *sharedFoo; @end |
然后您可以继续为此属性编写getter(实现将使用您建议的
1 | + (Foo *)sharedFoo { ... } |
这样做的好处是,如果一个快速用户使用它,他们会做如下的事情:
1 | let foo = Foo.shared |
注意,没有
顺便说一句,如果你看看苹果如何定义他们的单件产品,这就是他们采用的模式,例如他们的
1 | @property (class, readonly, strong) NSURLSession *sharedSession; |
另一个非常微小的互操作性考虑因素是单例的名称。最好是您可以合并类型的名称,而不是
1 2 | let foo = Foo.shared let manager = DatabaseManager.shared |
显然,如果您真的想使用
1 | @property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared); |
显然,在编写Objective-C代码时,我们不应该让快速的互操作性超过其他设计考虑,但是,如果我们能够编写优雅地支持这两种语言的代码,那就更好了。
我同意其他人的观点,他们指出,如果您希望这是一个真正的单一实例,开发人员不能/不应该(不小心)实例化自己的实例,那么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //Create Singleton +( instancetype )defaultDBManager { static dispatch_once_t onceToken = 0; __strong static id _sharedObject = nil; dispatch_once(&onceToken, ^{ _sharedObject = [[self alloc] init]; }); return _sharedObject; } //In it method -(instancetype)init { self = [super init]; if(self) { //Do your custom initialization } return self; } |
要创建线程安全的singleton,可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @interface SomeManager : NSObject + (id)sharedManager; @end /* thread safe */ @implementation SomeManager static id sharedManager = nil; + (void)initialize { if (self == [SomeManager class]) { sharedManager = [[self alloc] init]; } } + (id)sharedManager { return sharedManager; } @end |
这个博客很好地解释了objc/cocoa中的singleton。