【问题标题】:Convincing Swift that a function will never return, due to a thrown Exception说服 Swift 一个函数永远不会返回,因为抛出异常
【发布时间】:2015-03-05 22:03:01
【问题描述】:

因为 Swift 没有抽象方法,所以我正在创建一个默认实现无条件地引发错误的方法。这会强制任何子类覆盖抽象方法。我的代码如下所示:

class SuperClass {
    func shouldBeOverridden() -> ReturnType {
        let exception = NSException(
            name: "Not implemented!",
            reason: "A concrete subclass did not provide its own implementation of shouldBeOverridden()",
            userInfo: nil
        )
        exception.raise()
    }
}

问题:因为函数应该返回一个值,而上面的函数没有return语句,编译失败。我需要一些方法让编译器相信这个函数永远不可能完成执行,因为它总是会引发错误。

当所有的错误处理似乎都是库级别的,因此超出了编译器的理解范围时,如何在 Swift 中做到这一点?是否有任何语言级别的功能(希望优雅地)终止程序的执行?

【问题讨论】:

  • 虽然您已经通过解决方案自行回答了您的问题,但您正在尝试解决一个糟糕/错误的问题。 Swift 没有内置抽象方法是有原因的。

标签: swift compilation abstract throw control-flow


【解决方案1】:

听起来你正在做的事情最好通过创建一个协议并将shouldBeOverridden 设为必需方法,然后让你的类符合该协议来完成。 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html

【讨论】:

  • 这可行,但SuperClass 的所有子类中还有其他元素,最好使用SuperClass 中的共享实现来实现。特别是,我的所有“子类”(或符合协议的类,根据您的建议)都必须具有几个存储属性,最好不必为每个子类重新声明这些属性。
  • 我建议将这两种方法结合起来,并拥有一个协议和一个基类。抽象方法在 Objective-C 或 Swift 中并不常见,而且我还没有发现协议不是更好选择的案例。 Apple 有时也会使用这种组合方法(请参阅 UIBarPositioning)。
  • 好的,效果很好。它看起来有点冗长,但它是有道理的,而且很干净。
  • 另外,我发现抽象方法通常属于抽象对象。 Furniture 作为一个类没有意义,因为拥有seatingCapacity 属性对于抽象的家具概念没有意义。不过,Chair 确实如此,Couch 也是如此。因此,这两个符合家具协议而不是派生自家具基类的对象通常更有意义。像myCouch as? Furniture 这样的检查对协议的作用与对类的作用相同。我使用的一般经验法则是,“实例化 Furniture 对象有意义吗?”
【解决方案2】:

Swift 的@noreturn 属性将函数和方法标记为不返回给它们的调用者。

可能是最简单的例子,内置函数abort()的签名是:

@noreturn func abort()

这为编译器提供了它需要的所有信息。例如,以下将编译得很好:

func alwaysFail() -> Int {
    abort()
}

虽然alwaysFail()理论上会返回一个Int,但Swift知道调用abort()之后就不能继续执行了。

我的原始代码不起作用的原因是因为 NSException.raise 是一个 pre-Swift 方法,因此没有 @noreturn 属性。为了轻松解决这个问题,我可以使用abort():

func shouldBeOverridden() -> ReturnType {
    println("Subclass has not implemented abstract method `shouldBeOverridden`!")
    abort()
}

或者,如果我仍然想使用NSException,我可以定义一个具有适当属性的扩展

extension NSException {
    @noreturn func noReturnRaise() {
        self.raise()
        abort() // This will never run, but Swift will complain that the function isn't really @noreturn if I don't call a @noreturn function before execution finishes.
    }
}

作为第三种选择,我可以在NSException.raise() 之后使用从未调用过的abort() 来安抚编译器。较早的选项,使用extension,实际上只是执行此操作的抽象:

func shouldBeOverridden() -> ReturnType {
    let exception = NSException(
        name: "Not implemented!",
        reason: "A concrete subclass did not provide its own implementation of shouldBeOverridden()",
        userInfo: nil
    )
    exception.raise()
    abort() // never called
}

【讨论】:

    【解决方案3】:

    在 Xcode 8 beta 6 (Swift 3 beta 6) 中,您现在可以使用 Never 返回类型而不是 @noreturn 来指示函数不会返回给它的调用者:

    func crash() -> Never {
        fatalError("Oops")
    }
    

    【讨论】:

      【解决方案4】:

      如何创建一个返回 Never 的计算属性,称为 abstractoverrideMe 或类似的,如下所示:

         var abstract: Never { fatalError("Must be overridden") }
      

      使用示例:

      class AbstractTableViewCell: UITableViewCell {
          func configure(with model: Any) { abstract }
      }
      
      final class ContactTableViewCell: AbstractTableViewCell {
          override func configure(with model: Any) { 
              /* Real implementation here */
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-10-16
        • 2017-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-27
        相关资源
        最近更新 更多