目前我有一些像这样的快速代码:
class C {
let type: Type;
var num = 0;
init() {
self.type = Type({ (num: Int) -> Void in
self.num = num;
});
}
}
Swift编译器拒绝允许它,说我在初始化之前引用了self.type
,尽管这显然是完全不真实的。此外,我不能使用其他问题/答案中找到的解决方法,因为type
不是可选的,并且它是不可变的,因此无法首先用nil
无意义地初始化它。
如何让Swift编译器接受这个完全有效的代码?
这与早期从初始化程序返回无关。回调是异步执行的 - 它被存储然后再使用。
我还有一些let
在此之后被初始化。我必须将它们 all 变成可变选项,即使它们不是可选的也不能被变异。
答案 0 :(得分:10)
这有效:
class C {
var type: Type?;
var num = 0;
init() {
self.type = Type({ (num: Int) -> Void in
self.num = num;
});
}
}
我猜你知道的。但是你想知道你的版本无法正常工作的原因。
现在是棘手的部分:为行
self.num = num;
工作,编译器必须将self传递给闭包内部。闭包可能并且可能在Type的构造函数内执行。
这就像你写过
一样self.type = Type({ (self: C, num: Int) -> Void in
self.num = num
});
这在语法上是错误的,但解释了编译器编译代码时必须做的事情。
要将此必需的self实例传递给Type的构造函数,必须初始化self。但是self没有被初始化,因为你仍然在构造函数中。
当您尝试将self传递给Type的构造函数时,编译器会告诉您self的哪一部分未初始化。
<强> P.S。强>
很明显,Type在您的代码中知道num。 如果你想在C中使用let而不是var,你可以做...
class Type {
let num: Int
init () {
num = 3
}
}
class C {
let type: Type;
var num = 0;
init() {
self.type = Type();
num = type.num
}
}
甚至
class C {
let type: Type;
var num: Int {
return type.num
}
init() {
self.type = Type();
}
}
取决于您是否要更改num。这两个例子都编译没有错误。
答案 1 :(得分:4)
首先,重要的是要解释为什么这不是完全有效的代码,并且根本不清楚self.type
在初始化之前是否未被使用。请考虑以下代码扩展:
struct A {
init(_ f: (Int) -> Void) { f(1) }
}
class C {
let type: A
var num = 0 {
didSet { print(type) }
}
init() {
self.type = A({ (num: Int) -> Void in
self.num = num
})
}
}
如果您完成逻辑,则会注意到self.type
在初始化之前通过print
访问。斯威夫特目前无法证明这种情况不会发生,所以不允许这样做。 (理论上的Swift编译器可能会证明在某些特定情况下不会发生这种情况,但对于大多数非平凡的代码,它可能会遇到暂停问题。无论如何,当前的Swift编译器不够强大,无法实现这一点。证明,这是一个非平凡的证明。)
一种解决方案虽然有些不尽如人意,但却使用隐式解包的选项:
private(set) var type: A! = nil
除声明外,代码的每个其他部分都是相同的。您不必将其视为可选项。实际上,这只会关闭此变量的“初始化前使用”检查。它还令人遗憾地使它在当前文件中可设置,但确实使其对其他所有人都不可变。
这是我最常使用的技术,虽然我经常尝试重新设计系统,这样就不需要这种封闭(并不总是可行,但我经常试着试试)。它并不漂亮,但它是一致的并且是丑陋的。
另一种可以在某些情况下起作用的技术是懒惰:
class C {
lazy var type: A = {
A({ (num: Int) -> Void in self.num = num })}()
var num = 0
init() {}
}
有时它起作用,有时它不起作用。在你的情况下它可能。当它工作时,它非常好,因为它使属性真正不可变,而不仅仅是公开不可变,当然因为它不需要!
。
答案 2 :(得分:2)
有趣。
如果你避免在闭包中引用self
,看起来错误就会消失。
如果回调是同步的,您可以按照以下步骤更改您的代码:
class C {
let type: Type
var num = 0
init() {
var numTemp = 0 // create a temporary local var
let initialType = Type({ (num: Int) -> () in
numTemp = num // avoid self in the closure
});
self.type = initialType
self.num = numTemp
}
}
重要提示:如果闭包是异步,这将 NOT 。
使用Xcode(Playground)6.4 + Swift 1.2
进行测试希望这有帮助。
答案 3 :(得分:0)
正如appzYourLife所说,num
的临时变量就足够了:
class Type{
var y: (Int)->Void
init(y2:((Int)->Void)){
self.y = y2
}
}
class C {
let type: Type
var num: Int = 0
init() {
var num2 = 0
self.type = Type(y2: { (num3: Int) -> () in
num2 = num3
});
self.num = num2
}
}
但是,不需要type
的临时变量,此错误消息具有误导性。