Swift:“必须调用超类的指定初始化程序”错误,即使代码正在这样做

Posted

技术标签:

【中文标题】Swift:“必须调用超类的指定初始化程序”错误,即使代码正在这样做【英文标题】:Swift: "Must call a designated initializer of the superclass" error even though code is doing so 【发布时间】:2017-01-19 05:40:09 【问题描述】:

目标是继承 SCNNode。根据class docs,init(geometry geometry: SCNGeometry?) 是一个指定的初始化器(没有列出convenience 关键字),那么这段代码不是在调用其超类的指定初始化器吗?

为什么 Xcode 会显示以下错误?

必须调用超类 SCNNode 的指定初始化器

class PreviewNode: SCNNode 
    // Constants
    let PreviewNodeColor = gRedColor
    let Size = CGFloat(1.0)
    let ChamferRadius = CGFloat(0.0)

    override init() 
        let previewBox = SCNBox(width: Size, height: Size, length: Size, chamferRadius: ChamferRadius)
        previewBox.firstMaterial!.diffuse.contents = PreviewNodeColor
        previewBox.firstMaterial!.transparency = 0.2
        previewBox.firstMaterial!.specular.contents = UIColor.whiteColor()
        super.init(geometry: previewBox)
    

【问题讨论】:

@LeoDabus 嗨,您能否发布作为答案并解释为什么会发生错误,即使它似乎正在调用指定的初始化程序?谢谢! @LeoDabus 应该补充说我们之前尝试过您的方法,它有效,但想了解错​​误发生的原因。 为什么投反对票? 【参考方案1】:

问题在于您还试图在调用 self.init() 之前访问 PreviewNode 属性

试试这样:

Xcode 8 GM • Swift 3

class PreviewNode: SCNNode 
    let previewNodeColor: UIColor = .red
    let size: CGFloat = 1
    let chamferRadius: CGFloat = 0
    convenience override init() 
        self.init()
        let previewBox = SCNBox(width: size, height: size, length: size, chamferRadius: chamferRadius)
        previewBox.firstMaterial?.diffuse.contents = previewNodeColor
        previewBox.firstMaterial?.transparency = 0.2
        previewBox.firstMaterial?.specular.contents = UIColor.white
        self.geometry = previewBox
    

【讨论】:

好的,这是一个误导性错误?为什么需要convenience 关键字?你不能直接覆盖空的init函数吗? 在这种情况下你需要两者convenience override 介意解释一下原因吗?试图了解未来的情况。因为没有convenience 关键字似乎编译得很好。谢谢! 它覆盖了不执行自定义几何的原始空初始化。当您向该类添加另一个初始化程序时,您需要添加一个方便关键字。 是的,这就是为什么你会得到信用:) 但试图了解你是否需要convenienceself.init,或者按照答案中发布的方法是否同样有效。只是想更好地理解 Swift!【参考方案2】:

使用了这个答案,但 Leo Dabus 值得称赞。如果您能解释定义一个新的便利初始化器是否同样有效,或者像这个答案那样覆盖默认初始化器,或者一个比另一个更可取,请发表评论。

class PreviewNode: SCNNode 
    // Constants
    let MainColor = gRedColor
    let MainSize = CGFloat(1.0)
    let MainRadius = CGFloat(0.0)
    let MainTransparency = CGFloat(0.2)


    override init() 
        super.init()
        doInit()
    


    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    


    private func doInit() 
        let previewBox = SCNBox(width: MainSize, height: MainSize, length: MainSize, chamferRadius: MainRadius)
        previewBox.firstMaterial!.diffuse.contents = MainColor
        previewBox.firstMaterial!.transparency = MainTransparency
        previewBox.firstMaterial!.specular.contents = UIColor.whiteColor()
        self.geometry = previewBox
    

【讨论】:

【参考方案3】:

我对 Swift 4.2 指南的理解是,指定的初始化程序必须初始化所有存储的属性。方便的初始化器可以初始化属性的子集(这在不同情况下创建实例时可能很有用)但是,它们必须首先调用子类的指定初始化器,然后再调用超类的指定初始化器来初始化所有存储的属性在定制开始之前。 Swift 指南中的这张图表可能会有所帮助:

通过使用方便的关键字,Leo 可以调用 self.init(),它是 PreviewNode 的指定初始化程序,它反过来又调用 SCNNode 的指定初始化程序。这解决了我们都得到的错误:'required'初始化程序'init(coder :)'必须由'SCNNode'的子类提供。那是因为 UIView 采用了 NSCoding 协议。

下面是 Leo 代码的注释版本,显示了处理问题的三个选项。我认为它们都同样正确,但其中一个可能比其他更适合特定问题。

import SceneKit
class PreviewNode: SCNNode 
    let previewNodeColor: UIColor = .red
    let size: CGFloat = 1
    let chamferRadius: CGFloat = 0
    //convenience override init()    //Option 1 - use with self.init() to use the default designated initialisers up the inheritance ladder
    //override init()                //Option 2 - use with super.init() and the additional required initialiser below
    required init(coder aDecoder: NSCoder)   //Option 3 - Use with super.init() to initialise the geometry property of SCNNode and the required intialiser
        //self.init()
        super.init()
        let previewBox = SCNBox(width: size, height: size, length: size, chamferRadius: chamferRadius)
        previewBox.firstMaterial?.diffuse.contents = previewNodeColor
        previewBox.firstMaterial?.transparency = 0.2
        previewBox.firstMaterial?.specular.contents = UIColor.white
        self.geometry = previewBox
    
    //Use with Option 2 to provide the additional required initialiser for NSCoder
    /*
    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    */

很抱歉撞了一个旧帖子,但我遇到了同样的问题,研究很有趣 :-)

【讨论】:

以上是关于Swift:“必须调用超类的指定初始化程序”错误,即使代码正在这样做的主要内容,如果未能解决你的问题,请参考以下文章

从 Swift 2.3 迁移到 Swift 3 错误消息

Pod 安装错误后,Swift 3 结果出现编译错误

从 swift 1.2 迁移后 swift2 中的 healthKit 错误

从 swift 1.2 迁移后 swift2 中的 healthKit 错误

学习Swift -- 错误处理

将 swift 2.3 转换为 swift 3 错误