Swift中为何不推荐使用基于struct的单例,这类实现是否有误?
Swift 基于struct实现Decodable单例的问题解答
现有实现的问题
你的代码本身语法没有错误,也能完成基础的解码、属性读写逻辑,但完全不符合单例设计模式的核心要求,属于错误的单例实现:
- 单例的核心是全局唯一、所有修改共享生效,但struct是值类型,只要你把
Person.shared赋值给其他变量,就会触发拷贝生成全新的独立实例,两个实例的修改互不影响,完全违背单例的设计目的。示例如下:
var copyPerson = Person.shared copyPerson.age = 30 print(Person.shared.age) // 输出还是你之前设置的20,不会同步修改,说明是两个不同的实例
- 你调用
initData这类mutating方法时,要求Person.shared必须处于可变上下文,如果你在某些let约束的场景下使用会直接编译报错,使用限制非常多。 - 解码时你直接把新decode出来的实例赋值给
self,相当于完全替换了全局的shared实例,如果有其他位置已经持有了旧实例的拷贝,就会出现严重的数据不一致问题。
为什么不推荐用struct实现单例
struct作为值类型,和单例的需求天生不兼容:
- 单例需要保证全局唯一实例,但值类型的拷贝特性是Swift的默认行为,你无法阻止使用者拷贝生成新实例,根本没法保证唯一性
- 为了限制拷贝需要做大量额外的冗余处理,开发和维护成本远高于直接用class实现单例,完全没有必要
- 引用类型的class天生适配单例需求:赋值时只会传递引用指针,所有变量指向的都是堆上的同一个实例,修改全局共享生效。正确的class版本单例实现参考如下:
class Person: Decodable { static let shared = Person() private(set) var name: String? var age: Int? private init() {} func initData(from data: Data) { if let person = try? JSONDecoder().decode(Person.self, from: data) { self.name = person.name self.age = person.age } } }
内容的提问来源于stack exchange,提问作者user16114557
相关产品推荐
相关产品推荐

