在Swift 1.1中重写初始化程序的正确方法 [英] The correct way to override an initializer in Swift 1.1

查看:73
本文介绍了在Swift 1.1中重写初始化程序的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这曾经在Xcode 6.1 beta中起作用:

class MainViewController: NSViewController {
  convenience override init() {
    self.init(nibName: "MainView", bundle: nil)
  }
}

切换到6.1 GM2后,它无法编译.看起来问题与"convenience override init?(),convenience init?()override init?(),但都没有效果.

那么,到目前为止,重写此类初始化程序的正确方法是什么?

解决方案

您正试图通过委派给init?(nibName:bundle:)(这是一个失败的初始化器)来实现init()(一个不失败的初始化器).这是行不通的:如果super.init调用失败,您将得到一个未初始化的实例,Swift不允许这样做.

或者换句话说,使用失败的初始化程序的结果是可选的,并且您不能使用可选的值代替非可选的值.而且,在类初始化和继承的情况下,您不能将非可选的self替换为可选的—您只能将self的状态设置委托给其他初始化程序.

相反,您可以通过一点点歌舞来删除可选性/失败性:

class MainViewController: NSViewController {
    override init!(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        // check state here and provide app-specific diagnostic if it's wrong
    }
    convenience override init() {
        self.init(nibName: "MainView", bundle: nil)
    }

    // need this, too, or the compiler will complain that it's missing
    required init?(coder: NSCoder) {
        fatalError("not implemented") // ...or an actual implementation
    }
}

init!初始化器产生一个隐式展开的可选(IUO)—就像IUO类型可用于在使用可选值和非可选值的代码之间进行桥接一样,init!初始化器可以在故障和非可选值之间进行桥接-失败的初始值设定项.您不能从非失败的初始值设定项委派给失败的初始值设定项,但可以从非失败的初始值设定项委派给init!初始值设定项,再从init!初始值设定项委派给失败的初始值设定项.

在这里,您要使用的NSViewController初始化程序完全失败,因此您可以使用init!初始化程序覆盖它.然后,您可以声明一个委托给新的init!初始化程序的不可失败的convenience init.


我们通常倾向于避免IUO,并且通过扩展init!初始化程序来避免,因为我们通常希望明确允许(并要求处理)失败或明确禁止失败.但是,针对IUO及其同类的最强大的通用用例之一就是将仅在源代码之外保证的条件转换为断言,以使您的代码可以视为无误. IBOutlet是一个很好的例子-在nib/storyboard中,您保证IBOutlet变量的状态,但是编译器不知道这一点–包资源的其他任何处理. /p>

这种委派的小动作将失败的负担放在代码中特定且易于调试的位置上-如果从init()super.init(nibName:bundle:)的调用失败,则您的应用程序将崩溃.但是您可以期望该调用仅在非常特定的条件下(并且主要是在开发时)才会失败.

This used to work in Xcode 6.1 beta:

class MainViewController: NSViewController {
  convenience override init() {
    self.init(nibName: "MainView", bundle: nil)
  }
}

After I switch to 6.1 GM2, it doesn't compile. Looks like the issue is related to "failable initializers" introduced in Swift 1.1. I've tried convenience override init?(), convenience init?() and override init?(), neither worked.

So what's the correct way to override this kind of initializers as of today?

解决方案

You're trying to implement init() — a non-failable initializer — by delegating to init?(nibName:bundle:), which is a failable initializer. This doesn't work: if the super.init call fails, you'd be left with a non-initialized instance, which Swift won't allow.

Or to put it another way, the result of using a failable initializer is an optional, and you can't use an optional in place of a non-optional value. And in the case of class initialization and inheritance, you can't substitute a non-optional self for an optional one — you can only delegate the setup of self's state to a different initializer.

Instead, you can remove optionality/failability with a little song and dance:

class MainViewController: NSViewController {
    override init!(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        // check state here and provide app-specific diagnostic if it's wrong
    }
    convenience override init() {
        self.init(nibName: "MainView", bundle: nil)
    }

    // need this, too, or the compiler will complain that it's missing
    required init?(coder: NSCoder) {
        fatalError("not implemented") // ...or an actual implementation
    }
}

An init! initializer produces an implicitly unwrapped optional (IUO) — just as an IUO type can be used to bridge between code that works with optional and non-optional values, an init! initializer can bridge between failable and non-failable initializers. You can't delegate from a non-failable initializer to a failable initializer, but you can delegate from a non-failable initializer to an init! initializer and from an init! initializer to a failable initializer.

Here, the NSViewController initializer you want to use is fully failable, so you override it with an init! initializer. Then, you can declare a non-failable convenience init that delegates to your new init! initializer.


We often tend to avoid IUOs, and by extension init! initializers, because we generally want to either explicitly allow for (and require handling) failure or explicitly disallow it. However, one of the strongest general use cases for IUOs and their kin is to turn conditions that are guaranteed only outside of your source code into assertions that your code can treat as infallible. IBOutlets are a great example of this — in your nib/storyboard you guarantee the state of your IBOutlet variables, but the compiler doesn't know about that — as are just about anything else to do with bundle resources.

This little delegation dance puts the burden of failure at a specific, easily debuggable place in your code — if the call from init() to super.init(nibName:bundle:) fails, your app will crash. But you can expect that call to fail only in very specific (and mostly at-development-time) conditions.

这篇关于在Swift 1.1中重写初始化程序的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆