如果您可以针对 iOS 4.0 或更高版本
使用 GCD,它是在 Objective-C(线程安全)中创建单例的最佳方法吗?
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
instancetype
。使用它而不是 id
时,代码完成要好得多。
这是创建类实例的完全可接受且线程安全的方法。从技术上讲,它可能不是“单例”(因为这些对象只能有 1 个),但只要您只使用 [Foo sharedFoo]
方法访问该对象,这就足够了。
实例类型
instancetype
只是 Objective-C
的众多语言扩展之一,每个新版本都会添加更多。
知道,喜欢。
并以它为例,说明关注底层细节如何让您深入了解转换 Objective-C 的强大新方法。
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^
{
sharedInstance = [self new];
});
return sharedInstance;
}
+ (Class*)sharedInstance
{
static dispatch_once_t once;
static Class *sharedInstance;
dispatch_once(&once, ^
{
sharedInstance = [self new];
});
return sharedInstance;
}
MySingleton.h
@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
MySingleton.m
@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
init
不可用吗?
MySingleton
,例如在 MySingleton.m
我打电话给 [super alloc]
您可以避免通过覆盖 alloc 方法来分配类。
@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's docs on creating a singleton 以获取有关实现其他一些方法的提示,以确保在类选择不使用 sharedFoo 方法时只能创建一个。
如果您想确保 [[MyClass alloc] init] 返回与 sharedInstance 相同的对象(我认为这不是必需的,但有些人想要它),可以使用第二个 dispatch_once 轻松安全地完成:
- (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 的第一个调用者将执行“正常”初始化并将单例对象存储在 init 方法中。以后对 init 的任何调用都将完全忽略 alloc 返回的内容并返回相同的 sharedInstance; alloc 的结果将被释放。
+sharedInstance 方法将像往常一样工作。如果它不是第一个调用 [[MyClass alloc] init] 的调用者,那么 init 的结果不是 alloc 调用的结果,但没关系。
您问这是否是“创建单例的最佳方式”。
一些想法:
首先,是的,这是一个线程安全的解决方案。这种 dispatch_once 模式是在 Objective-C 中生成单例的现代、线程安全的方式。那里不用担心。但是,您问这是否是“最好”的方法。但是,应该承认 instancetype 和 [[self alloc] init] 与单例一起使用时可能会产生误导。 instancetype 的好处是它是一种明确的方式来声明类可以被子类化,而无需求助于 id 类型,就像我们在过去所做的那样。但是这种方法中的静态存在子类化挑战。如果 ImageCache 和 BlobCache 单例都是 Cache 超类的子类而不实现它们自己的 sharedCache 方法怎么办? ImageCache *imageCache = [ImageCache sharedCache]; // 精细 BlobCache *blobCache = [BlobCache sharedCache]; // 错误;这将返回前面提到的 ImageCache!!!为此,您必须确保子类实现它们自己的 sharedInstance(或您为特定类调用的任何名称)方法。底线,您原来的 sharedInstance 看起来它将支持子类,但它不会。如果您打算支持子类化,至少包括警告未来开发人员必须覆盖此方法的文档。为了与 Swift 实现最佳互操作性,您可能希望将其定义为属性,而不是类方法,例如:@interface Foo : NSObject @property (class, readonly, strong) Foo *sharedFoo; @end 然后你可以继续为这个属性编写一个 getter(实现将使用你建议的 dispatch_once 模式): + (Foo *)sharedFoo { ... } 这样做的好处是,如果 Swift 用户去使用它,他们会做类似的事情: let foo = Foo.shared 注意,没有 (),因为我们将它实现为一个属性。从 Swift 3 开始,这就是通常访问单例的方式。因此,将其定义为属性有助于促进这种互操作性。顺便说一句,如果你看看 Apple 是如何定义他们的单例的,这就是他们采用的模式,例如他们的 NSURLSession 单例定义如下: @property (class, readonly, strong) NSURLSession *sharedSession;另一个非常小的 Swift 互操作性考虑是单例的名称。最好是可以合并类型的名称,而不是 sharedInstance。例如,如果类是 Foo,您可以将单例属性定义为 sharedFoo。或者,如果该类是 DatabaseManager,您可以调用属性 sharedManager。然后 Swift 用户可以这样做: let foo = Foo.shared let manager = DatabaseManager.shared 显然,如果你真的想使用 sharedInstance,你可以随时声明 Swift 名称:@property (class, readonly, strong) Foo * sharedInstance NS_SWIFT_NAME(shared);显然,在编写 Objective-C 代码时,我们不应该让 Swift 互操作性超过其他设计考虑因素,但是,如果我们可以编写优雅地支持这两种语言的代码,那就更好了。我同意其他人的观点,如果您希望这是一个真正的单例,开发人员不能/不应该(意外地)实例化他们自己的实例,那么 init 和 new 上的不可用限定符是谨慎的。
@interface className : NSObject{
+(className*)SingleTonShare;
}
@implementation className
+(className*)SingleTonShare{
static className* sharedObj = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{
if (sharedObj == nil){
sharedObj = [[className alloc] init];
}
});
return sharedObj;
}
要创建线程安全的单例,您可以这样做:
@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
这个博客很好地解释了单例singletons in objc/cocoa
//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;
}