我们有一个子类 NSNotificationQueue
,它有一些自定义方法,可以在 dispatch_async
调用中排队通知,以便在主线程上退出。
我们有一个类方法 sharedQueue
,它使用 dispatch_once
和静态引用返回您的普通 Objective-C 风格单例。
我的问题是,如果我们从后台线程调用 sharedQueue
方法,那么单例是否会绑定(bind)到后台线程,如果该线程消失,单例是否也会被删除?如果是这样,我们是否应该确保在主线程上创建单例?
如果我们需要确保在主线程上创建单例,这是我们的方法:
+ (instancetype)sharedQueue
{
static dispatch_once_t onceToken;
static BCOVNotificationQueue *notificationQueue;
dispatch_once(&onceToken, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
notificationQueue = [[BCOVNotificationQueue alloc] initWithNotificationCenter:NSNotificationCenter.defaultCenter];
});
});
return notificationQueue;
}
最佳答案
What is the proper way to create a thread-safe singleton?
技术很简单:
+ (instancetype)sharedQueue {
static dispatch_once_t onceToken;
static BCONotificationQueue *sharedInstance;
dispatch_once(&onceToken, ^{
sharedInstance = [[BCONotificationQueue alloc] init];
});
return sharedInstance;
}
这是标准的、线程安全的单例实例化。
但是你说:
We have a subclassed
NSNotificationQueue
...
这解释了您将其分派(dispatch)到主队列的直觉(因为您正在处理 NSNotificationQueue
并且它特定于您调用它的线程)。但是你不希望你的单例同步调度到主队列。我建议您将单例本身的实例化(使用上述模式)与其所需的 NSNotificationQueue
分离。
让我们暂时假设,无论您在哪里调用 BCONotificationQueue
,您的意图都是发布到主线程。不是子类化 NSNotificationQueue
,而是让它成为一个不透明的 NSObject
,它的私有(private)实现封装了底层的 NSNotificationQueue
,如下所示:
// BCONotificationQueue.h
@import Foundation;
NS_ASSUME_NONNULL_BEGIN
@interface BCONotificationQueue: NSObject
@property (class, readonly) BCONotificationQueue *sharedQueue NS_SWIFT_NAME(shared);
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle;
@end
NS_ASSUME_NONNULL_END
和
// BCONotificationQueue.m
#import "BCONotificationQueue.h"
@interface BCONotificationQueue ()
@property (nonatomic, strong) NSNotificationQueue *queue;
@end
@implementation BCONotificationQueue
+ (BCONotificationQueue *)sharedQueue {
static dispatch_once_t onceToken;
static BCONotificationQueue *sharedInstance;
dispatch_once(&onceToken, ^{
sharedInstance = [[BCONotificationQueue alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
if ((self = [super init])) {
dispatch_async(dispatch_get_main_queue(), ^{
self.queue = [[NSNotificationQueue alloc] initWithNotificationCenter:NSNotificationCenter.defaultCenter];
});
}
return self;
}
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle {
dispatch_async(dispatch_get_main_queue(), ^{
[self.queue enqueueNotification:notification postingStyle:postingStyle];
});
}
@end
因此,我们将像使用 Objective-C 一样实例化我们的单例,但在幕后我们将异步调度包装的 NSNotificationQueue
的实例化(避免任何死锁风险)回到主队列。包装的 enqueueNotification
将做同样的事情,确保所有通知队列操作都发生在主(串行)队列上,同时仍然享受 BCONotificationQueue
包装器的单例行为。
关于ios - 创建线程安全单例的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55385352/