本文共 2369 字,大约阅读时间需要 7 分钟。
细心的读者可能会发现,在我们调试一些纯Swift 类型出现类似数组越界这样的情况时,我们在控制台得到的报错信息
会和传统调试NSObject 子类时不太一样
,比如在使用NSArray 时:
let array: NSArray = [1, 2, 3]array[100]// 输出// *** Terminating app due to uncaught exception 'NSRangeException', // reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: // index 100 beyond bounds [0 .. 2]'
而如果我们使用Swift 类型的话:
let array = [1, 2, 3]array[100]// 输出// Fatal error: Index out of range: // file Swift/ContiguousArrayBuffer.swift, line 444
这是因为,Swift 是没有异常机制的,纯Swift 类型也不会抛出异常。在调试时我们可以使用断言
来排除类似这样的问题,但是断言只会在Debug 环境中有效,而在Release 编译中所有的断言都将被禁用。在因为输入错误导致程序无法继续运行的时候,我们一般考虑以产生致命错误(fatalError
)的方式来终止程序。
fatalError 的使用非常简单,它的API 和断言的比较类似:
public func fatalError(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) -> Never
这可以帮助编译器进行一些检查:
enum MyEnum { case Value1, Value2, Value3}func check(someValue: MyEnum) -> String { switch someValue { case .Value1: return "OK" case .Value2: return "Maybe OK" default: // 这个分支没有返回 String,也能编译通过 fatalError("Should not show!") }}
我们在实际编程中,经常会有不想让别人调用某个方法,但又不得不将其暴露出来的时候。一个最常见并且合理的需求就是“抽象类型或者抽象函数”
。在很多语言中都有这样的特性:父类定义了某个方法,但是自己并不给出具体实现,而是要求继承它的子类去实现这个方法,而在Objective-C 和Swift 中都没有这样的抽象函数
语法直接支持,虽然在Cocoa 中对于这类需求我们有时候会转为依赖接口
和委托的
设计模式来变通的实现,但是其实Apple 自己再Cocoa 中也有很多类似抽象函数的设计。比如UIActivity 的子类必须要实现一大堆指定的方法,而正因为缺少抽象函数机制,这些方法都必须在文档中写明。
在面对这种情况时,为了确保子类实现这些方法,同时父类中的方法不被错误的调用,我们就可以利用fatalError
来在父类中强制抛出错误
,以保证使用这些代码的开发者留意到他们必须在自己的子类中实现相关方法:
class MyClass { func methodMustBeImplementedInSubclass() { fatalError("这个方法必须在子类中被重写") }}class YourClass: MyClass { override func methodMustBeImplementedInSubclass() { print("YourClass 实现了该方法") }}class TheirClass: MyClass { func someOtherMethod() { }}YourClass().methodMustBeImplementedInSubclass()// 输出// YourClass 实现了该方法 TheirClass().methodMustBeImplementedInSubclass()// 输出// Fatal error: 这个方法必须在子类中被重写: file Swift_1/ViewController.swift, line 12
不过一个好消息是Apple 意识到了抽象函数这个特性的缺失,所以很可能在今后的Swift 版本中我们能看到这个语法特性的加入。
不仅仅是在对于类似抽象函数的使用中可以选择fatalError
,对于其他一切我们不希望别人随意调用,但是又不得不去实现的方法,我们都应该使用fatalError
来避免
任何可能的误会
。比如父类标明了某个init 方法
是required
的,但是你的子类永远不会使用这个方法来初始化时,就可以采用类似的方式,被广泛使用(以及被广泛讨厌的)init?(coder: NSCoder)
就是一个例子。在子类中,我们往往会写:
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented")}
可以用其避免编译错误
转载地址:http://frnuk.baihongyu.com/