Swift中最令我沮丧的情况之一就是当我有这样一个类时:
class A: NSObject {
var a: Int
override init() {
clear()
super.init()
}
func clear() {
a = 5
}
}
当然,这会导致多个编译器错误('self' used in method call 'clear' before 'super.init' call
和Property 'self.a' not initialized at super.init call
。
更改构造函数行的顺序只能修复第一个错误:
override init() {
super.init()
clear()
}
通常,我最终会做两件事之一。第一个选项是使a
成为一个隐式解包的可选项,但我觉得这样很糟糕,因为a
在构造函数返回后永远不会是nil
:
var a: Int! = nil
第二个选项是在构造函数中复制clear()
函数的功能:
override init() {
a = 5
super.init()
}
这适用于这个简化的示例,但它在不复杂的代码库中不必要地重复了很多代码。
有没有办法初始化a
而不重复我的clear
功能或使a
成为可选项?如果没有,为什么不呢?!
答案 0 :(得分:3)
"为什么不"在这个特定的案例中非常简单。你写的东西可以让我写下这个:
a
然后final
将不会被初始化。所以你所写的东西永远不会是合法的Swift。
那就是说,有一个更深层的版本可能是合法的,但不是。如果您标记了类!
或者这是一个结构,那么编译器可能能够通过内联所有可能的方法调用来证明所有代码路径都已正确初始化,但编译器并没有今天做的一切;它太复杂了。编译器只是说"我的校对引擎并不强大,所以要把它关掉。"
IMO,这里的正确解决方案是= nil
类型,但我不会添加class A: NSObject {
var a: Int! // You don't have to assign `!` types; they're automatically nil
override init() {
super.init()
clear()
}
func clear() {
a = 5
}
}
。那是误导性的。我会这样写:
a
这表示"我负责确保!
在使用之前将被设置。"这正是你在做什么。当我需要将自己作为代表传递时,我会一直这样做。我希望编译器可以在每个方法调用中探索每个可能的代码路径,但是今天它没有(并且考虑到编译时可能会做什么,也许我不希望如此)。
但我觉得这是一种糟糕的风格,因为在构造函数返回
之后永远不会是nil
这正是nil
类型的要点。当任何其他对象可以获得它时,它们永远永远不会是零。如果它可能是!
,那么您就不应该使用!
。我同意class A: NSObject {
var a = Int.min // -1 might be reasonable. I just like it to be clearly wrong
override init() {
super.init()
clear()
}
func clear() {
a = 5
}
}
危险(因为编译器不再帮助你),但它的风格并不差。
IMO唯一合理的方法是分配默认值。我当然不会使用实际值;那将是一个微妙的错误的邀请。我会使用一些默认值来实现目标。
!
我并不喜欢这种方法,尽管我已经在一些地方使用过它。我认为它不会给你!
带来任何安全感(它不会崩溃,但是你几乎肯定会给你带来微妙的行为错误,我宁愿这样做崩溃)。这是程序员必须注意的地方,因为编译器足够强大以证明一切正确,而{{1}}的重点是标记这些地方。