首页 > 解决方案 > 可以在不使用“静态”的情况下创建单例类吗?

问题描述

我们使用Static声明singleton,因此只会创建一个实例。是否可以在不使用的情况下声明单例Static?如果是,可以覆盖实例吗?

class SingletonClass {
    static let shared = SingletonClass();
    func requestToAccess() {
        // Print statement
    }
}

标签: swiftstaticsingleton

解决方案


这里有很多问题,所以让我们先解决这些问题:

  1. 这是无效的 Swift 代码。Class并且Static两者都必须小写。
  2. Swift 中的类型名称应该是 UpperCamelCase。
  3. 格式很奇怪。

解决这个问题,我们得到:

class SingletonClass {
    static let shared = SingletonClass()

    func requestToAccess() {
        print(SingletonClass.shared)
    }
}

您在这里拥有的是一个共享实例,但实际上并不是一个单例。单身人士的关键定义特征是它很好……单身。这里根本不是这样,因为绝对没有什么能阻止我说:

let myOwnInstance = SingletonClass()

单例通常用于对单一物理资源的状态进行建模。如果存在两个实例,它们可能会相互干扰。考虑这个例子,一个(有缺陷的)单例试图在一个硬件上模拟单个 LED:

public class UserActivityIndicatorLED {
    public static let shared = UserActivityIndicatorLED()

    public private(set) var currentState: Bool = false {
        didSet {
            if currentState { turnLEDOn() }
            else { turnLEDOff() }
        }
    }

    public func toggle() { self.currentState.toggle() }
}

存在“只写”事物的情况并不少见,其中您有一个用于设置值的 API(例如微控制器的数字输出引脚的开/关状态),但没有用于检查状态的相应 API . 在这种情况下,您的程序需要通过将状态保存到变量来记住状态,并确保“记住的状态”和真实硬件始终一起更新。

此实现确保正确完成,因为turnLEDOn并且turnLEDOff只能通过改变 currentState 来调用。但是,由于违反了单例属性,因此可能会发生这种情况:

UserActivityIndicatorLED.shared().toggle() // => UserActivityIndicatorLED.shared().currentState becomes true, LED turns on

let myInstance = UserActivityIndicatorLED() // => I create a new instance, violating the singleton pattern
myInstance.toggle() // myInstance.currentState becomes true, LED is made to turn on again (it just stays on)
myInstance.toggle() // myInstance.currentState becomes false, LED is turned off, but UserActivityIndicatorLED.shared().currentState is still true!


// Now the system's "memory" of the physical state is desynchronized from the
// "true hardware" state, because creating a new instance of `UserActivityIndicatorLED`
// permitting the mutation of the hardware state without a corresponding update to the
// memorized state.
// Some user calls this again, expecting the LED to turn off, but surprise, it's already off!
UserActivityIndicatorLED.shared().toggle() // UserActivityIndicatorLED.shared().currentState becomes false, but the light was already off

为了解决这个问题,并确保你实际上有一个单例,初始化器需要设为私有,这样新实例只能在 内创建SingletonClass,并且对初始化器的唯一调用是shared变量:

class SingletonClass {
    static let shared = SingletonClass()

    private init() { }

    func requestToAccess() {
        print(SingletonClass.shared)
    }
}

我需要使用静态变量吗?

不一定,您可以使用全局变量,但更糟糕的是:

let SingletonClassShared = SingletonClass()

class SingletonClass {
    fileprivate init() { }

    func requestToAccess() {
        print(SingletonClass.shared)
    }
}

但是您是否需要某种形式的静态存储(全局变量、静态存储、类存储)。除非有实例,否则实例存储(存储属性)实际上不会分配内存。而且由于没有实例可以存储单例引用,因此它没有任何意义。


推荐阅读