Как правильно создать потокобезопасный синглтон?
Техника проста:
+ (instancetype)sharedQueue {
static dispatch_once_t onceToken;
static BCONotificationQueue *sharedInstance;
dispatch_once(&onceToken, ^{
sharedInstance = [[BCONotificationQueue alloc] init];
});
return sharedInstance;
}
Это стандартный, потокобезопасныйсоздание экземпляров синглетонов.
Но вы говорите:
У нас есть подкласс NSNotificationQueue
...
Это объясняет вашу интуицию о том, как отправить этоосновная очередь (потому что вы имеете дело с NSNotificationQueue
, и это зависит от того, к какому потоку вы его вызвали).Но вы не хотите, чтобы ваш синглтон синхронно отправлялся в главную очередь.Я бы предложил вам отделить создание экземпляра самого синглтона (используя вышеуказанный шаблон) от необходимого ему NSNotificationQueue
.
Давайте на секунду предположим, что вы намеревались публиковать сообщения в главном потокенезависимо от того, где вы вызвали BCONotificationQueue
.Вместо того, чтобы создавать подклассы NSNotificationQueue
, вы вместо этого просто делаете его непрозрачным NSObject
, чья частная реализация оборачивает базовый 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
.