JuJ*_*oDi 201 compiler-errors properties swift
我有两节课,Shape和Square
class Shape {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Run Code Online (Sandbox Code Playgroud)
通过上面的实现,我得到错误:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
Run Code Online (Sandbox Code Playgroud)
为什么我必须self.sideLength在打电话前设置super.init?
Rub*_*ben 164
引用Swift编程语言,它回答了你的问题:
"Swift的编译器执行四个有用的安全检查,以确保完成两阶段初始化而没有错误:"
安全检查1"指定的初始化程序必须确保在委托一个超类初始化程序之前初始化其类引入的所有属性."
摘录自:Apple Inc."The Swift Programming Language."iBooks. https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11
Hit*_*nki 95
Swift具有在初始化器中完成的非常清晰,特定的操作序列.让我们从一些基本的例子开始,一直到一般情况.
我们来看一个对象A.我们将其定义如下.
class A {
var x: Int
init(x: Int) {
self.x = x
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,A没有超类,因此它不能调用super.init()函数,因为它不存在.
好了,现在让我们用一个名为B的新类子类A.
class B: A {
var y: Int
init(x: Int, y: Int) {
self.y = y
super.init(x: x)
}
}
Run Code Online (Sandbox Code Playgroud)
这与Objective-C不同,[super init]后者通常首先被调用.斯威夫特不是这样.在执行任何其他操作之前,您有责任确保实例变量处于一致状态,包括调用方法(包括超类'初始化程序).
小智 36
初始化所有实例变量后,应调用"super.init()".
在Apple的"中级Swift"视频中(您可以在Apple Developer视频资源页面https://developer.apple.com/videos/wwdc/2014/中找到它),大约在28:40,它明确表示所有初始化器都在初始化实例变量后必须调用超类.
在Objective-C中,情况正好相反.在Swift中,由于所有属性都需要在使用之前进行初始化,因此我们需要首先初始化属性.这是为了防止从超类的"init()"方法调用覆盖函数,而不首先初始化属性.
所以"Square"的实现应该是:
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
self.sideLength = sideLength
numberOfSides = 4
super.init(name:name) // Correct position for "super.init()"
}
func area () -> Double {
return sideLength * sideLength
}
}
Run Code Online (Sandbox Code Playgroud)
Ban*_*tor 33
来自文档
安全检查1
指定的初始值设定项必须确保在委托一个超类初始化程序之前初始化其类引入的所有属性.
为什么我们需要这样的安全检查?
要回答这个问题,请通过swift中的初始化过程进行操作.
两相初始化
Swift中的类初始化是一个两阶段的过程.在第一阶段,每个存储的属性都由引入它的类分配初始值.一旦确定了每个存储属性的初始状态,第二阶段就开始了,并且每个类都有机会在新实例被认为可以使用之前进一步定制其存储的属性.
使用两阶段初始化过程可以使初始化安全,同时仍然为类层次结构中的每个类提供完全的灵活性.两阶段初始化可防止在初始化属性值之前访问它们,并防止属性值被另一个初始化程序意外地设置为不同的值.
因此,为了确保按照上面的定义完成两步初始化过程,有四个安全检查,其中一个是,
安全检查1
指定的初始值设定项必须确保在委托一个超类初始化程序之前初始化其类引入的所有属性.
现在,两阶段初始化从不谈论订单,但是这个安全检查super.init在所有属性初始化之后引入了有序.
安全检查1可能看起来无关紧要,因为 两阶段初始化可以防止在初始化之前访问属性值,而无需进行此安全检查1.
就像这个样本一样
class Shape {
var name: String
var sides : Int
init(sides:Int, named: String) {
self.sides = sides
self.name = named
}
}
class Triangle: Shape {
var hypotenuse: Int
init(hypotenuse:Int) {
super.init(sides: 3, named: "Triangle")
self.hypotenuse = hypotenuse
}
}
Run Code Online (Sandbox Code Playgroud)
Triangle.init在使用之前已经初始化了每个属性.所以安全检查1似乎无关紧要,
但那可能会有另一种情况,有点复杂,
class Shape {
var name: String
var sides : Int
init(sides:Int, named: String) {
self.sides = sides
self.name = named
printShapeDescription()
}
func printShapeDescription() {
print("Shape Name :\(self.name)")
print("Sides :\(self.sides)")
}
}
class Triangle: Shape {
var hypotenuse: Int
init(hypotenuse:Int) {
self.hypotenuse = hypotenuse
super.init(sides: 3, named: "Triangle")
}
override func printShapeDescription() {
super.printShapeDescription()
print("Hypotenuse :\(self.hypotenuse)")
}
}
let triangle = Triangle(hypotenuse: 12)
Run Code Online (Sandbox Code Playgroud)
输出:
Shape Name :Triangle
Sides :3
Hypotenuse :12
Run Code Online (Sandbox Code Playgroud)
这里如果我们调用了super.init之前设置的那个hypotenuse,那么super.init调用就会调用printShapeDescription(),因为它已被覆盖,它将首先回退到Triangle类的实现printShapeDescription().该printShapeDescription()三角类的访问hypotenuse仍然没有被初始化非可选属性.并且这是不允许的,因为两阶段初始化会阻止在初始化属性值之前访问它们
因此,确保按照定义完成两阶段初始化,需要有一个特定的调用顺序super.init,也就是说,在初始化了self类引入的所有属性之后,我们需要进行安全检查1
fnc*_*c12 14
抱歉丑陋的格式化.只需在声明后添加一个问题字符,一切都会好的.一个问题告诉编译器该值是可选的.
class Square: Shape {
var sideLength: Double? // <=== like this ..
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Run Code Online (Sandbox Code Playgroud)
EDIT1:
有一种更好的方法可以跳过此错误.根据jmaschad的评论,没有理由在你的情况下使用可选项因为选项在使用中不舒服而且你必须在访问它之前检查optional是否不是nil.所以你要做的就是在声明后初始化成员:
class Square: Shape {
var sideLength: Double=Double()
init(sideLength:Double, name:String) {
super.init(name:name)
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Run Code Online (Sandbox Code Playgroud)
EDIT2:
在得到这个答案后,我找到了更好的方法.如果希望在构造函数中初始化类成员,则必须在构造函数内和super.init()调用之前为其分配初始值.像这样:
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
self.sideLength = sideLength // <= before super.init call..
super.init(name:name)
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Run Code Online (Sandbox Code Playgroud)
祝学习Swift好运.
爱德华,
您可以像这样修改示例中的代码:
var playerShip:PlayerShip!
var deltaPoint = CGPointZero
init(size: CGSize)
{
super.init(size: size)
playerLayerNode.addChild(playerShip)
}
Run Code Online (Sandbox Code Playgroud)
这是使用隐式解包的可选项.
在文档中我们可以阅读:
"与可选项一样,如果在声明隐式解包的可选变量或属性时未提供初始值,则其值自动默认为nil."
Swift不会允许你初始化超类而没有初始化属性,反向Obj C.所以你必须在调用"super.init"之前初始化所有属性.
请访问http://blog.scottlogic.com/2014/11/20/swift-initialisation.html.它为您的问题提供了一个很好的解释.
在声明的末尾添加nil。
// Must be nil or swift complains
var someProtocol:SomeProtocol? = nil
// Init the view
override init(frame: CGRect)
super.init(frame: frame)
...
Run Code Online (Sandbox Code Playgroud)
这适用于我的情况,但可能不适用于您的情况
| 归档时间: |
|
| 查看次数: |
97790 次 |
| 最近记录: |