Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift-使用前未初始化变量(但未使用)_Swift_Closures_Initializer - Fatal编程技术网

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,尽管这显然是完全不真实的。此外,我不能使用其他问题/答案中的变通方法,因为类型不

目前我有一些类似这样的swift代码:

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可以在其他地方使用之前,必须对其进行初始化。