我希望在Swift中做一些我已经习惯在其他多种语言中做的事情:使用自定义消息抛出一个运行时异常。例如(在Java中):
throw new RuntimeException("A custom message here")
我知道可以抛出符合ErrorType协议的枚举类型,但我不想为每种错误类型都定义一个枚举。理想情况下,我想尽可能地模仿上面的示例。我尝试创建一个实现ErrorType 协议的自定义类,但我甚至无法确定该协议需要什么。有什么想法吗?
我希望在Swift中做一些我已经习惯在其他多种语言中做的事情:使用自定义消息抛出一个运行时异常。例如(在Java中):
throw new RuntimeException("A custom message here")
我知道可以抛出符合ErrorType协议的枚举类型,但我不想为每种错误类型都定义一个枚举。理想情况下,我想尽可能地模仿上面的示例。我尝试创建一个实现ErrorType 协议的自定义类,但我甚至无法确定该协议需要什么。有什么想法吗?
enum MyError: Error {
case runtimeError(String)
}
func someFunction() throws {
throw MyError.runtimeError("some message")
}
do {
try someFunction()
} catch MyError.runtimeError(let errorMessage) {
print(errorMessage)
}
Error
类型,最通用的类型将是NSError
,你可以创建一个工厂方法来创建并抛出一个带有自定义消息的错误。errorMessage
中的String
,如果可以,我该如何做到这一点? - Renan CamaforteString
与MyError.RuntimeError
相关联(在throw
时设置),您可以在catch
中访问它(使用let errorMessage
)。 - Arkkutry!
,在这里没有被使用。实际上,在进行可能会抛出异常的调用之前,你甚至不能不使用某种形式的try
。 (此外,代码的那部分是示例用法,而不是实际解决方案。) - Arkku最简单的方法是使String
符合Error
:
extension String: Error {}
然后你可以直接传入一个字符串:
throw "Some Error"
为了使字符串本身成为错误的localizedString
,您可以扩展LocalizedError
:extension String: LocalizedError {
public var errorDescription: String? { return self }
}
Redundant conformance of 'String' to protocol 'Error'
:( - Alexander Borisenkoerror.localizedDescription
时,它会说无法完成操作。 - Noah Allen@nick-keets的解决方案最优雅,但在测试目标中出现了以下编译时错误:
Redundant conformance of 'String' to protocol 'Error'
这里是另一种方法:
struct RuntimeError: LocalizedError {
let description: String
init(_ description: String) {
self.description = description
}
var errorDescription: String? {
description
}
}
使用方法:
throw RuntimeError("Error message.")
重要提示:已更新为使用LocalizedError
而非Error
。使用Error
并覆盖localizedDescription
将无法返回正确的描述。相反,它会显示如下内容:The operation couldn’t be completed. (__lldb_expr_39.RuntimeError error 1.)
Swift 4:
根据以下链接:
https://developer.apple.com/documentation/foundation/nserror
如果您不想定义自定义异常,您可以使用标准的NSError对象,如下所示:
import Foundation
do {
throw NSError(domain: "my error domain", code: 42, userInfo: ["ui1":12, "ui2":"val2"] )
}
catch let error as NSError {
print("Caught NSError: \(error.localizedDescription), \(error.domain), \(error.code)")
let uis = error.userInfo
print("\tUser info:")
for (key,value) in uis {
print("\t\tkey=\(key), value=\(value)")
}
}
输出:
Caught NSError: The operation could not be completed, my error domain, 42
User info:
key=ui1, value=12
key=ui2, value=val2
这使您可以提供自定义字符串(错误域),加上数字代码和包含所需的所有附加数据的字典,可以是任何类型。
注意:此测试在OS = Linux(Ubuntu 16.04 LTS)上进行。
error.localizedDescription
,但该描述在throw语句中未设置为任何内容。因此,最终只会得到一个通用的“无法完成操作”的错误。 - Edward Breyerror.localizedDescription
事件时,我从NSError中获取域和代码。在控制台中,我看到了一个遵循此模式的消息:The operation couldn’t be completed. (\(domain) error \(code).)
。这对我来说看起来不错,感谢PJ的分享。 - sergeyski.comuserInfo
字典中的NSLocalizedDescriptionKey
,这是提供消息字符串的文档方式。 - bshirley看看这个很酷的版本。该想法是实现String和ErrorType协议并使用错误的rawValue。
enum UserValidationError: String, Error {
case noFirstNameProvided = "Please insert your first name."
case noLastNameProvided = "Please insert your last name."
case noAgeProvided = "Please insert your age."
case noEmailProvided = "Please insert your email."
}
使用方法:
do {
try User.define(firstName,
lastName: lastName,
age: age,
email: email,
gender: gender,
location: location,
phone: phone)
}
catch let error as User.UserValidationError {
print(error.rawValue)
return
}
as User.UserValidationError
,而且还需要 .rawValue
。但是,如果你将 CustomStringConvertible
实现为 var description: String { return rawValue }
,那么使用枚举语法获取自定义描述可能会很有用,而无需在每个打印它的地方都经过 rawValue
。 - Arkku最简单的解决方案,不需要额外的扩展、枚举、类等:
NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()
raise()
而非 throw
),很难记忆。相比之下,将代码中所有抛出异常的地方都替换成 throw Foo.Bar("baz")
或 throw "foo"
,我认为只需要付出一次性的一行代码扩展或枚举就足够了,比如使用 NSExceptionName
的方式要好得多。 - ArkkupostNotification
需要 2-3 个参数,其选择器与此类似。您是否在每个项目中覆盖 Notification
和/或 NotificationCenter
以允许它接受较少的输入参数? - Vyachaslav GerchicovString
扩展到符合Error
是否太令人惊讶,或者MyError
枚举是否太模糊(个人认为两者都是肯定的,而应该为每个错误单独设置一个枚举情况,即throw ThisTypeOfError.thisParticularCase
)。 - Arkku如果您不需要捕获错误并且想立即停止应用程序,可以使用fatalError:fatalError("自定义消息")
根据@Nick Keets的答案,这里提供一个更完整的示例:
extension String: Error {} // Enables you to throw a string
extension String: LocalizedError { // Adds error.localizedDescription to Error instances
public var errorDescription: String? { return self }
}
func test(color: NSColor) throws{
if color == .red {
throw "I don't like red"
}else if color == .green {
throw "I'm not into green"
}else {
throw "I like all other colors"
}
}
do {
try test(color: .green)
} catch let error where error.localizedDescription == "I don't like red"{
Swift.print ("Error: \(error)") // "I don't like red"
}catch let error {
Swift.print ("Other cases: Error: \(error.localizedDescription)") // I like all other colors
}
这篇文章最初发表在我的Swift博客上:http://eon.codes/blog/2017/09/01/throwing-simple-errors/
这篇文章讲解了关于IT技术中的错误处理。throw NSError(message: "err", code: 0)
- Sentry.cocatch let error where error.localizedDescription == "I don't like red"
是脆弱的,这将是一个很好的候选项,可以使用强类型错误枚举解决。另外一个解决方案在全局符合 String
到 Error
时显示潜在问题。 - stef首先,让我们看一些使用示例,然后再了解如何使这些示例起作用(定义)。
do {
throw MyError.Failure
} catch {
print(error.localizedDescription)
}
或者更具体的样式:
do {
try somethingThatThrows()
} catch MyError.Failure {
// Handle special case here.
} catch MyError.Rejected {
// Another special case...
} catch {
print(error.localizedDescription)
}
此外,分类是可行的:
do {
// ...
} catch is MyOtherErrorEnum {
// If you handle entire category equally.
} catch let error as MyError {
// Or handle few cases equally (without string-compare).
switch error {
case .Failure:
fallthrough;
case .Rejected:
myShowErrorDialog(error);
default:
break
}
}
public enum MyError: String, LocalizedError {
case Failure = "Connection fail - double check internet access."
case Rejected = "Invalid credentials, try again."
case Unknown = "Unexpected REST-API error."
public var errorDescription: String? { self.rawValue }
}
Swift会自动定义error
变量, 处理程序只需要读取localizedDescription
属性。
但是这种方式比较模糊,我们应该使用"catch MyError.Failure {}
"的风格(明确处理哪种情况),尽管像使用示例中所示的分类也是可行的。
Teodor-Ciuraru的答案(几乎相同)仍需要进行长时间的手动转换(例如 "catch let error as User.UserValidationError { ... }
")。
接受的分类枚举方法存在缺陷:
String
消息!(只是为了了解确切的错误)。catch MyError.runtimeError(let errorMessage) { ... }
。NSException方法具有分类枚举方法的相同缺点(除了可能更短的捕获段落),即使将其放在工厂方法中创建和抛出,也是相当复杂的。
通过简单地使用LocalizedError
而不是Error
, 该解决方案已经完善了其他现有的解决方案,并希望能够像我一样为某些人节省阅读所有其他帖子的时间。
(我的懒惰有时会给我带来很多工作。)
import Foundation
import XCTest
@testable import MyApp
class MyErrorTest: XCTestCase {
func testErrorDescription_beSameAfterThrow() {
let obj = MyError.Rejected;
let msg = "Invalid credentials, try again."
XCTAssertEqual(obj.rawValue, msg);
XCTAssertEqual(obj.localizedDescription, msg);
do {
throw obj;
} catch {
XCTAssertEqual(error.localizedDescription, msg);
}
}
func testThrow_triggersCorrectCatch() {
// Specific.
var caught = "None"
do {
throw MyError.Rejected;
} catch MyError.Failure {
caught = "Failure"
} catch MyError.Rejected {
caught = "Successful reject"
} catch {
caught = "Default"
}
XCTAssertEqual(caught, "Successful reject");
}
}
#1 如果为每个enum
实现errorDescription
很麻烦,则可以为所有enum
实现一次,例如:
extension RawRepresentable where RawValue == String, Self: LocalizedError {
public var errorDescription: String? {
return self.rawValue;
}
}
LocalizedError
的枚举,但可以删除"Self: LocalizedError
"部分,使其适用于任何字符串枚举。FileNotFound
怎么办?请参见我的其他帖子:
https://dev59.com/jl0Z5IYBdhLWcg3wbwCY#70448052
基本上,将上面链接中的LocalizedErrorEnum
复制并添加到您的项目中,并根据需要重复使用关联枚举。我喜欢@Alexander-Borisenko的回答,但是当作为错误捕获时,本地化描述没有返回。看起来你需要使用LocalizedError:
struct RuntimeError: LocalizedError
{
let message: String
init(_ message: String)
{
self.message = message
}
public var errorDescription: String?
{
return message
}
}
请查看此答案以获取更多详细信息。