ios - 创建线程安全单例的正确方法是什么?

标签 ios objective-c multithreading singleton

我们有一个子类 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/

相关文章:

iOS swift ImagePickerController : Improving Edit Mode

ios - 是否可以在不删除整个项目的情况下删除 iOS 钥匙串(keychain)中帐户项目的密码?

ios - 使用单个共享后台线程进行 iOS 数据处理?

C 多线程 : What is the advantage of a Read Lock (pthread_rwlock_rdlock) if all threads can access it simultaneously?

iphone - 如何调试或查看USB被占用的iOS设备的NSLog输出?

ios - 用动画替换segue?

iphone - `[super viewDidLoad]` 约定

iphone - 只有在 View 出现后才自动开始计算

java - 让java程序在没有线程的情况下 hibernate

iphone - 从 ZBar 转到另一个 View 时出错