Swift-使用前未初始化变量(但未使用)
目前我有一些类似这样的swift代码:Swift-使用前未初始化变量(但未使用),swift,closures,initializer,Swift,Closures,Initializer,目前我有一些类似这样的swift代码: class C { let type: Type; var num = 0; init() { self.type = Type({ (num: Int) -> Void in self.num = num; }); } } Swift编译器拒绝允许它,说我在初始化它之前引用了self.type,尽管这显然是完全不真实的。此外,我不能使用其他问题/答案中的变通方法,因为类型不
class C {
let type: Type;
var num = 0;
init() {
self.type = Type({ (num: Int) -> Void in
self.num = num;
});
}
}
Swift编译器拒绝允许它,说我在初始化它之前引用了self.type
,尽管这显然是完全不真实的。此外,我不能使用其他问题/答案中的变通方法,因为类型
不是可选的,并且是不可变的,因此不能毫无意义地首先使用nil来初始化它
如何使Swift编译器接受此完全有效的代码
这与早期从初始值设定项返回无关。回调是异步执行的-它被存储,然后在以后使用
我还有一些后续的let
s,它们在这之后初始化。我必须把它们都变成可变的可选项,即使它们不是可选的,也不能变异。有趣
如果避免在闭包中引用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
}
}
重要提示:如果闭包是异步的,这将不起作用
使用Xcode(游乐场)6.4+Swift 1.2进行测试
希望这能有所帮助。正如Appzyorlife所说,为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
}
}
但是,您不需要为类型
使用临时变量,此错误消息具有误导性 这是有效的:
class C {
var type: Type?;
var num = 0;
init() {
self.type = Type({ (num: Int) -> Void in
self.num = num;
});
}
}
我想你知道的。但是你想知道为什么你的版本不起作用
现在让我们来看看棘手的部分:线路
self.num = num;
为了工作,编译器必须将self传递到闭包内部。闭包可以并且可能在类型的构造函数内部执行
这好像是你写的
self.type = Type({ (self: C, num: Int) -> Void in
self.num = num
});
这在语法上是错误的,但解释了编译器编译代码时必须执行的操作
要将这个必要的self实例传递给类型为的构造函数,必须初始化self。但是self没有初始化,因为您仍然在构造函数中
当您尝试将self传递给类型的构造函数时,编译器会告诉您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。这两个示例编译时都没有错误。首先,重要的是要解释为什么这不是完全有效的代码,并且根本不清楚在初始化之前是否没有使用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() {}
}
有时有效,有时无效。在你的情况下可能是这样。当它工作时,它非常好,因为它使属性真正不可变,而不仅仅是公开不可变,当然,因为它不需要代码>请问什么是“类型”?其实并不重要。任何在构造函数中接受该签名回调的类型。]你是对的,我删除了它。闭包确实是完全异步的。可能是,但肯定不是,因为我在C之前不到五分钟编写了类型。哦,即使是,已访问的self
部分已初始化。正在使用的self部分已初始化。但它必须完全初始化才能被传递,事实并非如此。原则上,您可以很容易地引用未初始化或部分初始化的内容。这只是一个问题,你必须在多大程度上打败Swift编译器,直到它放弃并停止抱怨自己没有头脑。是的,如果你使用var和optionals。但是您使用let而不使用optional,在self可以在其他地方使用之前,必须对其进行初始化。