在斯威夫特类错误:属性在super.init调用未初始化在斯威夫特类错误:属性在super.init

2019-05-09 05:31发布

我有两个班, ShapeSquare

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
    }
}

随着实施上述我得到的错误:

property 'self.sideLength' not initialized at super.init call
    super.init(name:name)

我为什么要设置self.sideLength之前调用super.init

Answer 1:

从雨燕编程语言,它回答您的问题引用:

“雨燕的编译器执行四种有益的安全检查,以确保两相初始化完成,没有错误:”

安全检查1“指定初始化必须确保所有的‘通过其类介绍属性之前将其委托到一个超类初始化初始化’。

摘自:苹果公司“雨燕编程语言。”的iBooks。 https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11



Answer 2:

斯威夫特有在初始化完成的操作非常明确的,具体的序列。 让我们先从一些基本的例子,我们的方式到一个一般的情况。

让我们看一个物体答:我们将如下定义它。

class A {
    var x: Int
    init(x: Int) {
        self.x = x
    }
}

请注意,一个没有超类,所以它不存在,无法调用super.init()函数。

好了,现在让我们用一个名为B.新类的子类一

class B: A {
    var y: Int
    init(x: Int, y: Int) {
        self.y = y
        super.init(x: x)
    }
}

这是Objective-C的其中一个出发[super init]通常会首先别的之前调用。 不是这样的斯威夫特。 您有责任确保自己的实例变量是一致的状态,你做任何事情之前,包括调用方法(包括你的父类的初始化)。



Answer 3:

该“super.init()”初始化所有的实例变量后,应调用。

在苹果的“中级雨燕”的视频(你可以找到它在苹果开发者视频资源页https://developer.apple.com/videos/wwdc/2014/ ),约28:40,这是明确表示,所有在初始化当你初始化实例变量的超类必须调用。

在Objective-C,它是相反。 在斯威夫特,因为所有的属性需要使用数据之前进行初始化,我们需要先初始化属性。 这是为了防止超一流的“的init()”方法来overrided函数的调用,而无需首先初始化属性。

因此,“广场”的实施应该是:

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
    }
}


Answer 4:

从文档

安全检查1

一个指定的初始化必须确保所有通过它的类引入的属性都才代表了一个超类初始化初始化。


为什么我们需要一个安全检查这样吗?

要回答这个问题让我们去虽然迅速初始化过程。

两相初始化

在夫特类初始化是一个两阶段的过程。 在第一阶段中,每个所存储的属性分配由引入它的类的初始值。 一旦每个存储属性的初始状态已经确定,第二阶段开始,每类有机会定制之前,进一步的新实例被视为准备使用它的存储性能。

使用两阶段初始化过程使得初始化安全,同时还让完整的灵活性,以每类类层次结构。 两相初始化防止它们被初始化之前被访问的属性值 ,并且防止由另一个初始化被设置为不同的值出乎意料的属性值。

因此,为了确保两个步骤的初始化过程如上定义完成后,有四个安全检查,其中之一是,

安全检查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
    }
}

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)

输出:

Shape Name :Triangle
Sides :3
Hypotenuse :12

在这里,如果我们只好叫来了super.init设置之前hypotenuse ,则super.init通话将不得不叫printShapeDescription()并自认为已被重写那就先退回到三角类实现的printShapeDescription()printShapeDescription()的三角级接入的hypotenuse仍尚未初始化非可选属性。 而这是不允许的两相初始化防止它们被初始化之前被访问的属性值

因此,确保这两个阶段初始化完成定义,需要有调用的特定顺序super.init ,那就是,通过初始化介绍的所有属性后self类,因此我们需要一个安全检查1



Answer 5:

对不起,丑陋的格式。 只要把一个问题字符声明之后,一切都会好的。 一个问题告诉编译器值是可选的。

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
    }
}

EDIT1:

有一种更好的方式来跳过此错误。 据jmaschad的评论没有理由在你的情况的原因自选使用可选的未使用的舒适,你总是要检查是否可选是不能访问它之前为零。 因此,所有你需要做的就是申报后,初始化成员:

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
    }
}

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
    }
}

好运气在学习斯威夫特。



Answer 6:

SWIFT强制你初始化每个成员VAR之前,它是有史以来/可能曾经被使用。 由于不能确定时,它是超级计算机反过来会发生什么,它的错误了:防患于未然



Answer 7:

爱德华,

您可以修改代码在你的例子是这样的:

var playerShip:PlayerShip!
var deltaPoint = CGPointZero

init(size: CGSize)
{
    super.init(size: size)
    playerLayerNode.addChild(playerShip)        
}

这是使用隐式展开可选。

在文档我们可以看到:

“与自选,如果你没有在声明的隐含展开可选变量或财产提供一个初始值,其值自动默认为无。”



Answer 8:

雨燕不会让你初始化超类初始化出来的属性,OBJ C的反向所以,你必须调用“super.init”之前初始化所有属性。

请到http://blog.scottlogic.com/2014/11/20/swift-initialisation.html 。 它提供了一个很好的解释你的问题。



Answer 9:

添加零的声明结束。


// Must be nil or swift complains
var someProtocol:SomeProtocol? = nil

// Init the view
override init(frame: CGRect)
    super.init(frame: frame)
    ...

这个工作对我的情况,但可能不适合你的工作



Answer 10:

你只是INITING以错误的顺序。

     class Shape2 {
        var numberOfSides = 0
        var name: String
        init(name:String) {
            self.name = name
        }
        func simpleDescription() -> String {
            return "A shape with \(numberOfSides) sides."
        }
    }

    class Square2: Shape2 {
        var sideLength: Double

        init(sideLength:Double, name:String) {

            self.sideLength = sideLength
            super.init(name:name) // It should be behind "self.sideLength = sideLength"
            numberOfSides = 4
        }
        func area () -> Double {
            return sideLength * sideLength
        }
    }


Answer 11:

这是一个令人吃惊的愚蠢的设计。

考虑是这样的:

.
.
.
var playerShip:PlayerShip
var deltaPoint = CGPointZero

init(size: CGSize)
{
    super.init(size: size)
    playerShip = PlayerShip(pos: CGPointMake(self.size.width / 2.0, 100))
    playerLayerNode.addChild(playerShip)        
}
.
.
.

这是无效的,如上所述。 但这样的:

.
.
.
var playerShip:PlayerShip = PlayerShip(pos: CGPointMake(self.size.width / 2.0, 100))
var deltaPoint = CGPointZero

init(size: CGSize)
{
    super.init(size: size)
    playerLayerNode.addChild(playerShip)        
}
.
.
.

因为“自我”并没有被初始化。

我真诚地希望这个错误很快被固定。

(是的,我知道我可以创建一个空的对象,然后设置大小,但是这只是愚蠢)。



文章来源: Error in Swift class: Property not initialized at super.init call