我没有读太多关于Swift的文章,但有一件事我注意到,没有例外。那么,他们如何在Swift中进行错误处理呢?有没有人发现任何与错误处理有关的东西?
发布于 2014-06-04 05:56:37
Swift 2&3
Swift 2中的情况发生了一些变化,因为有一种新的错误处理机制,这与异常有些相似,但细节不同。
1.指示错误的可能性
如果函数/方法想表明它可能抛出错误,那么它应该包含像下面这样的throws关键字
func summonDefaultDragon() throws -> Dragon注意:函数实际上可以抛出的错误类型没有规范。这个声明只是声明函数可以抛出实现ErrorType的任何类型的实例,或者根本不抛出。
2.调用可能引发错误的函数
为了调用函数,需要使用try关键字,如下所示
try summonDefaultDragon()这一行通常应该在下面这样的do-catch块中。
do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}注意: catch子句使用Swift模式匹配的所有强大功能,因此您在这里非常灵活。
如果要从本身标记为throws关键字的函数调用抛出函数,则可以决定传播错误:
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
} 或者,您可以使用try?调用抛出函数。
let dragonOrNil = try? summonDefaultDragon()这样,您可以获得返回值或零(如果发生任何错误)。使用这种方式,您将不会获得error对象。
这意味着您还可以将try?与有用的语句组合起来,例如:
if let dragon = try? summonDefaultDragon()或
guard let dragon = try? summonDefaultDragon() else { ... }最后,您可以确定错误不会实际发生(例如,因为您已经检查了“先决条件”),并使用try!关键字:
let dragon = try! summonDefaultDragon()如果函数实际上抛出了一个错误,那么您将在应用程序中得到一个运行时错误,并且应用程序将终止。
3.抛出错误
为了抛出一个错误,您可以像下面这样使用抛出关键字
throw DragonError.dragonIsMissing您可以抛出符合ErrorType协议的任何内容。首先,NSError符合此协议,但您可能希望使用基于枚举的ErrorType,它使您能够对多个相关错误进行分组,可能还会使用其他数据块,如下所示
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}新的Swift 2&3错误机制与Java/C#/C++类型异常的主要区别如下:
do-catch + try + defer与传统的try-catch-finally语法不同。do-catch块不会捕获任何NSException,反之亦然,因为您必须使用ObjC。NSError方法惯例兼容,即返回false (用于Bool返回函数)或nil (用于AnyObject返回函数),并传递包含错误详细信息的NSErrorPointer。作为一种额外的语法糖来简化错误处理,还有两个概念。
defer关键字),它允许您实现与Java/C#/etc中的最后块相同的效果guard关键字),它允许您编写比正常错误检查/信令代码少多少if/ let代码。Swift 1
运行时错误:
正如Leandros在处理运行时错误(如网络连接问题、解析数据、打开文件等)所建议的那样,您应该像在ObjC中那样使用ObjC,因为基金会、AppKit、UIKit等都以这种方式报告它们的错误。所以与其说是语言,不如说是框架。
另一个常用的模式是分隔成功/失败块,如在AFNetworking中:
var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})仍然失败块经常接收NSError实例,描述错误。
程序员错误:
对于程序员错误(如数组元素的越界访问、传递给函数调用的无效参数等),您在ObjC中使用了异常。Swift语言似乎对异常没有任何语言支持(如throw、catch等关键字)。但是,如文档所示,它运行在与ObjC相同的运行时,因此仍然能够像这样抛出NSExceptions:
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()您只是无法在纯Swift中捕获它们,尽管您可能选择在ObjC代码中捕获异常。
问题是您是否应该为程序员错误抛出异常,或者像Apple在语言指南中建议的那样使用断言。
发布于 2014-06-03 08:44:33
Swift中没有任何例外,类似于Objective的方法.
在开发中,您可以使用assert捕捉任何可能出现的错误,并且需要在开始生产之前进行修复。
传统的NSError方法不会改变,您可以发送一个NSErrorPointer,它会被填充。
简短的例子:
var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
println("An error occurred \(error)")
} else {
println("Contents: \(contents)")
}发布于 2014-11-05 04:06:39
推荐的“快速方式”是:
func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}
var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
println("write failure 1: \(writeError!.localizedDescription)")
// assert(false) // Terminate program
}然而,我更喜欢尝试/捕捉,因为我发现它更容易跟踪,因为它将错误处理移动到另一个块的末尾,这种安排有时被称为“黄金路径”。幸运的是,您可以使用闭包来完成这一任务:
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}此外,添加重试工具也很容易:
TryBool {
write("~/Error3")(error: $0) // The code to try
}.retry {
println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
return write("~/Error3r") // The code to retry
}.catch {
println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}TryBool的清单是:
class TryBool {
typealias Tryee = NSErrorPointer -> Bool
typealias Catchee = NSError? -> ()
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return self.retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) {
var error: NSError?
for numRetries in 0...retries { // First try is retry 0
error = nil
let result = tryee(&error)
if result {
return
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
catchee(error)
}
}您可以编写类似的类来测试可选的返回值,而不是Bool值:
class TryOptional<T> {
typealias Tryee = NSErrorPointer -> T?
typealias Catchee = NSError? -> T
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) -> T {
var error: NSError?
for numRetries in 0...retries {
error = nil
let result = tryee(&error)
if let r = result {
return r
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
return catchee(error)
}
}TryOptional版本强制执行一种非可选的返回类型,使后续编程更容易。“快速的方式:
struct FailableInitializer {
init?(_ id: Int, error: NSErrorPointer) {
// Always fails in example
if error != nil {
error.memory = NSError(domain: "", code: id, userInfo: [:])
}
return nil
}
private init() {
// Empty in example
}
static let fallback = FailableInitializer()
}
func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
return FailableInitializer(id, error: error)
}
var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
println("failableInitializer failure code: \(failError!.code)")
failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap使用TryOptional:
let failure2 = TryOptional {
failableInitializer(2)(error: $0)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
let failure3 = TryOptional {
failableInitializer(3)(error: $0)
}.retry {
println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
return failableInitializer(31)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}注意自动展开。
https://stackoverflow.com/questions/24010569
复制相似问题