MST
星途 面试题库

面试题:Swift 单例模式在多线程环境下的问题及解决方案

Swift 的单例模式在多线程环境中可能会遇到哪些问题?请举例说明,并给出相应的解决方案,解释每种方案如何确保单例的唯一性和线程安全性。
48.2万 热度难度
编程语言Swift

知识考点

AI 面试

面试题答案

一键面试

问题

  1. 初始化竞争问题:在多线程环境下,多个线程可能同时判断单例对象是否已初始化,若都判断为未初始化,就会导致多次初始化单例,破坏单例的唯一性。例如,线程A和线程B同时进入了if (singleton == nil)判断语句块,然后两个线程都去创建单例实例。

解决方案

  1. 使用dispatch_once(适用于iOS和macOS平台)
    • 确保唯一性和线程安全性原理dispatch_once是Grand Central Dispatch(GCD)提供的函数,它会保证代码块只被执行一次,无论有多少线程同时调用。在Swift中使用如下:
class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // 这里可以进行一些初始化操作
        return instance
    }()
    private init() {}
}
- 这里`dispatch_once`的逻辑被`static let`声明的常量初始化器所隐式包含。由于`sharedInstance`是类级别的常量,它在第一次访问时被初始化,并且初始化过程是线程安全的,保证了单例的唯一性。

2. 使用@Synchronized(在一些特定情况下可用): - 确保唯一性和线程安全性原理@Synchronized关键字可以用来同步方法或代码块,使得同一时间只有一个线程能够执行被同步的代码。例如:

class Singleton {
    static var sharedInstance: Singleton?
    static func getInstance() -> Singleton {
        @Synchronized(self) {
            if sharedInstance == nil {
                sharedInstance = Singleton()
            }
            return sharedInstance!
        }
    }
    private init() {}
}
- 在这个例子中,`@Synchronized(self)`确保了在`getInstance`方法中的代码块在同一时间只有一个线程可以执行,从而避免了多个线程同时初始化单例的问题,保证了单例的唯一性和线程安全性。但这种方式由于引入了锁机制,在高并发场景下可能会影响性能。

3. 基于os_unfair_lock实现(性能优化方向): - 确保唯一性和线程安全性原理os_unfair_lock是一种轻量级的锁机制。在Swift中可以如下实现:

import Darwin

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        return instance
    }()
    private static var onceToken: Int = 0
    private static var lock = os_unfair_lock()
    static func getInstance() -> Singleton {
        if onceToken == 0 {
            os_unfair_lock_lock(&lock)
            defer { os_unfair_lock_unlock(&lock) }
            if onceToken == 0 {
                sharedInstance = Singleton()
                onceToken = 1
            }
        }
        return sharedInstance
    }
    private init() {}
}
- 首先通过`onceToken`进行快速判断,如果已经初始化则直接返回。当`onceToken`为0时,加锁进一步判断并初始化,这样既减少了锁的竞争,又保证了单例的唯一性和线程安全性,相比`@Synchronized`有更好的性能表现。