【问题标题】:What's the difference between Struct based and Class based singletons?基于结构的单例和基于类的单例有什么区别?
【发布时间】:2022-02-24 11:03:12
【问题描述】:

这两种方法是否相同或是否存在需要注意的主要差异/陷阱:

class MyClassSingleton {
  static let sharedInstance = MyClassSingleton()
  private init(){}

  func helloClass() { print("hello from class Singleton") }
}

struct MyStructSingleton {
  static let sharedInstance = MyStructSingleton()
  private init() {}

  func helloStruct() { print("hello from struct Singleton") }
}

【问题讨论】:

    标签: swift


    【解决方案1】:

    主要区别在于基于类的可变单例可以工作,而基于结构的可变“单例”不能。除非你想让你的单例不可变(这很少见),否则你应该坚持基于类的。

    这是一个基于可变结构的“单例”如何不起作用的说明。考虑为两个单例添加一个可变成员 state,如下所示:

    class MyClassSingleton {
        static let sharedInstance = MyClassSingleton()
        private init(){}
        var state = 5
        func helloClass() { print("hello from class Singleton: \(state)") }
    }
    
    struct MyStructSingleton {
        static let sharedInstance = MyStructSingleton()
        private init() {}
        var state = 5
        func helloStruct() { print("hello from struct Singleton: \(state)") }
    }
    

    我将state 设为var,但我可以将其公开为只读属性和变异方法;最重要的是这两种类型现在都是可变的。

    如果我这样做

    let csi = MyClassSingleton.sharedInstance
    csi.state = 42
    MyClassSingleton.sharedInstance.helloClass()
    

    42 被打印出来,因为csi 正在引用共享实例。

    但是,当我对基于结构的单例做同样的事情时

    var ssi = MyStructSingleton.sharedInstance
    ssi.state = 42
    MyStructSingleton.sharedInstance.helloStruct()
    

    改为打印 5,因为 ssisharedInstance 的副本,这当然表明我们的单例实际上不是单例。

    【讨论】:

    • 有趣的是,使用static var sharedInstance 而不是static let sharedInstance 使它看起来有效。每次我调用MyStructSingleton.sharedInstance 时都会生成一个新的结构副本吗?
    • @RunningTurtle 当您声明MyStructSingleton 类型的新变量时,会创建MyStructSingleton 的新副本。一旦将sharedInstance 分配给它,两者就会变得相同,但它们不是同一个实例。当您将MyStructSingleton 作为参数传递或从方法中返回时,也会发生同样的事情。
    • 实际上,你可以使用结构体编写可变单例。您只需要直接在sharedInstance 上应用所有突变,然后瞧。
    【解决方案2】:

    这取决于您想要实现的目标以及您希望如何使用基于classstruct 之间的差异的结构。您将看到的最常见的事情是使用带有单例对象的类。

    Singleton 几乎相同,它们只创建一次,但您会从 classstruct 获得不同的行为,因为:

    • 类是引用类型,而结构是值类型
    • 结构用于定义简单结构
    • 结构不能被继承

    还有几个不同之处,但您可以从中了解。

    【讨论】:

      【解决方案3】:

      正如另一个答案中已经提到的,类和结构具有不同的属性。最重要的是,类具有引用语义,而结构具有值语义。

      尽管如此,全局变量(或静态属性)引入了引用语义,即使它是在结构中定义的,这表明您确实可以使用结构实现(可变)单例。

      Sergey Kalinichenko 说明的问题是,将结构的值分配给另一个变量将创建副本而不是引用(请记住,结构具有值语义)。因此副本上的突变将不适用于原始值。如果你直接改变你的结构的静态属性,你的单例确实会被修改。

      struct Singleton {
      
        private init() {}
        var state = 5
        static var sharedInstance = MyStructSingleton()
      
      }
      
      Singleton.sharedInstance.state = 10
      print(Singleton.sharedInstance.state) // 10
      

      【讨论】:

        猜你喜欢
        • 2013-03-15
        • 2021-12-06
        • 1970-01-01
        • 2016-04-19
        • 2013-09-24
        • 2017-04-30
        • 1970-01-01
        • 1970-01-01
        • 2012-09-07
        相关资源
        最近更新 更多