Как правильно создать потокобезопасный синглтон? - PullRequest
1 голос
/ 27 марта 2019

У нас есть подкласс NSNotificationQueue, в котором есть несколько пользовательских методов, которые ставят в очередь уведомление внутри вызова dispatch_async, чтобы выйти в основной поток.

У нас есть метод класса sharedQueue, который возвращает ваш средний синглтон в стиле Objective-C, используя dispatch_once и статическую ссылку.

У меня вопрос, если мы вызовем sharedQueueметод из фонового потока - это то, что синглтон затем привязывается к фоновому потоку, и должен ли этот поток исчезнуть, будет ли синглтон также удален?Если да, то должны ли мы создать синглтон в главном потоке?

Это наш подход, если нам нужно убедиться, что синглтон создан в главном потоке:

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

1 Ответ

1 голос
/ 28 марта 2019

Как правильно создать потокобезопасный синглтон?

Техника проста:

+ (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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...