Swift(Xcode)中初始化阶段赋值的属性应使用可选类型还是临时对象?
初始化阶段必赋值的类属性方案选择
针对你提到的grimoireMemento属性场景,首先要明确:你给出的两种实现都不是最优方案,先逐个说问题:
两种方案的缺陷
方案1:声明为普通可选类型
class Kokoro{ private var emot:String = "" var pain:[String:Int] = [:] private var grimoireMemento:GrimoireMemento? var toHeart:[String:String] = [:] var fromHeart:[String:String] = [:] var standBy:Bool = false init(absDictionaryDB: AbsDictionaryDB) { self.grimoireMemento = GrimoireMemento(absDictionaryDB: absDictionaryDB) } func getPain(biJuuName: String) -> Int { return pain[biJuuName] ?? 0 } func getGrimoireMemento()->GrimoireMemento{ return self.grimoireMemento! } }
这个方案的问题很明显:
- 你明明确定初始化完成后这个属性100%有值,却要在取值时写强制解包,后续如果有人改了初始化逻辑漏赋值,这里会直接触发运行时崩溃,编译器完全帮你拦不住。
- 可选类型的语义是「这个值可能为nil」,和你的实际设计完全不符,后续维护的人看到可选类型会平白增加不必要的空判断逻辑。
方案2:提前赋值临时默认对象
class Kokoro{ private var emot:String = "" var pain:[String:Int] = [:] var grimoireMemento:GrimoireMemento = GrimoireMemento(absDictionaryDB: AbsDictionaryDB()) var toHeart:[String:String] = [:] var fromHeart:[String:String] = [:] var standBy:Bool = false init(absDictionaryDB: AbsDictionaryDB) { self.grimoireMemento = GrimoireMemento(absDictionaryDB: absDictionaryDB) } func getPain(biJuuName: String) -> Int { return pain[biJuuName] ?? 0 } }
这个方案比上一个问题更大:
- 为了凑编译器的初始化检查,你凭空构造了一个完全没用的临时
GrimoireMemento实例,连带着还要造一个无意义的空AbsDictionaryDB对象,纯纯浪费初始化性能。 - 如果
GrimoireMemento或AbsDictionaryDB的构造方法有副作用——比如写本地文件、注册全局通知、上报埋点,这个临时对象的构造会直接触发预期外的逻辑,出了bug极难排查。 - 代码语义非常混乱,后续维护的人看到默认值的空DB实例,根本猜不到你是为了凑初始化规则随便写的,还是真的有什么兜底逻辑。
正确实现方式
你明确提到这个属性会在初始化阶段完成赋值,生命周期内不会为nil,根本不需要在两种烂方案里做选择:
直接把它声明为非可选类型,不需要给默认值,只要在类的所有指定初始化器里,在初始化完成前给这个属性赋值即可。编译器会自动做静态检查,如果你漏了赋值,会直接编译失败,根本不会把问题带到运行时。
正确代码示例:
class Kokoro{ private var emot:String = "" var pain:[String:Int] = [:] // 直接声明为非可选,不需要默认值 private var grimoireMemento: GrimoireMemento var toHeart:[String:String] = [:] var fromHeart:[String:String] = [:] var standBy:Bool = false init(absDictionaryDB: AbsDictionaryDB) { // 在init完成前给属性赋值即可,编译器会校验 self.grimoireMemento = GrimoireMemento(absDictionaryDB: absDictionaryDB) } func getPain(biJuuName: String) -> Int { return pain[biJuuName] ?? 0 } // 取值的时候不需要解包,直接返回就行 func getGrimoireMemento() -> GrimoireMemento { return self.grimoireMemento } }
如果你的场景更特殊——比如属性无法在初始化器里拿到参数赋值,但保证在第一次被使用前一定会完成赋值(比如iOS里ViewController的依赖注入属性,在viewDidLoad前就会被赋值),可以用隐式解析可选类型(GrimoireMemento!),但你当前的初始化阶段赋值场景完全不需要。
如果非要在你给出的两个方案里二选一,选第一个可选类型的方案,至少没有无意义的对象构造和潜在副作用风险,但还是强烈建议用上面的标准非可选实现。
内容的提问来源于stack exchange,提问作者yotamarker
相关产品推荐
相关产品推荐

