我有两门课,形状和正方形

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。调用super.init之前的sidelth ?


Swift强制你在使用/可能使用之前初始化每个成员var。因为它不能确定当它变成超级时发生了什么,所以它会出错:小心总比后悔好


引用自Swift编程语言,它回答了你的问题:

Swift的编译器会执行四项有用的安全检查来确保这一点 该两阶段初始化完成无错误: 安全检查1 "指定的初始化器必须确保所有的 类引入的属性在它之前初始化 委托到超类初始化式。” 摘自:苹果公司《快速编程语言》。“iBooks。 https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11


对不起,格式很难看。 只要在声明后加上一个问题字符,一切都会好起来的。 一个问题告诉编译器这个值是可选的。

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的评论,没有理由在你的情况下使用可选的,因为可选的是不舒服的使用,你总是要检查如果可选的不是nil之前访问它。所以你所要做的就是在声明后初始化member:

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

祝你学习Swift好运。


Swift在初始化器中有一个非常清晰、特定的操作序列。让我们从一些基本的例子开始,一直到一般的情况。

让我们以一个对象a为例,我们将这样定义它。

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

注意,A没有超类,所以它不能调用super.init()函数,因为它不存在。

好,现在我们用一个新类B来子类A。

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

这与Objective-C不同,Objective-C通常首先调用[super init]。但在Swift中并非如此。在执行其他操作之前,包括调用方法(包括父类的初始化式),您有责任确保实例变量处于一致的状态。


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

在Apple的“Intermediate 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
    }
}

爱德华,

你可以像这样修改你的例子中的代码:

var playerShip:PlayerShip!
var deltaPoint = CGPointZero

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

这使用了一个隐式解包装的可选选项。

在文档中我们可以读到:

与可选选项一样,如果你不提供初始值 声明一个隐式展开的可选变量或属性,它是 Value自动默认为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)
    ...

这对我的案子有用,但对你的可能没用


来自文档

安全检查一 指定的初始化式必须确保所有属性 类引入的初始化 超类初始值设定项。


我们为什么需要这样的安全检查?

为了回答这个问题,让我们在swift中经历初始化过程。

Two-Phase Initialization Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use. The use of a two-phase initialization process makes initialization safe, while still giving complete flexibility to each class in a class hierarchy. Two-phase initialization prevents property values from being accessed before they are initialized, and prevents property values from being set to a different value by another initializer unexpectedly.

为了确保两步初始化过程按照上面定义的那样完成,有四个安全检查,其中一个是,

安全检查一 指定的初始化式必须确保所有属性 类引入的初始化 超类初始值设定项。

现在,两相初始化从来不讲顺序,但是这个安全检查,引入了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
    }
}

三角形。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

Here if we had called the super.init before setting the hypotenuse, the super.init call would then have called the printShapeDescription() and since that has been overridden it would first fallback to Triangle class implementation of printShapeDescription(). The printShapeDescription() of Triangle class access the hypotenuse a non optional property that still has not been initialised. And this is not allowed as Two-phase initialization prevents property values from being accessed before they are initialized

因此,确保两阶段初始化是按照定义完成的,需要有一个特定的顺序调用super。init,也就是在初始化self类引入的所有属性之后,因此我们需要一个安全检查1


你只是开始的顺序不对。

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

应该是这样的:

init(sideLength:Double, name:String) {
    self.sideLength = sideLength
    super.init(name:name)
    numberOfSides = 4
}

看看这个链接: https://swiftgg.gitbook.io/swift/swift-jiao-cheng/14_initialization#two-phase-initialization


@Janos如果你让属性是可选的,你不需要在init中初始化它。- - - - - -

这对我很管用。