如何处理未明确抛出的方法或代码的错误?
将其包装为 do / catch 块会导致编译器警告:
"'catch' block is unreachable because no errors are thrown in 'do' block"
来自 C# / JAVA 背景,这至少可以说是一件奇怪的事情。作为一名开发人员,我应该能够在 do/catch 块中保护和包装任何代码块。仅仅因为一个方法没有明确地用“throw”标记并不意味着不会发生错误。
如何处理未明确抛出的方法或代码的错误?
将其包装为 do / catch 块会导致编译器警告:
"'catch' block is unreachable because no errors are thrown in 'do' block"
来自 C# / JAVA 背景,这至少可以说是一件奇怪的事情。作为一名开发人员,我应该能够在 do/catch 块中保护和包装任何代码块。仅仅因为一个方法没有明确地用“throw”标记并不意味着不会发生错误。
您所问的问题在 Swift 中是不可能的,因为 Swift 无法处理运行时错误,例如越界、访问冲突或运行时强制解包失败。如果发生任何这些严重的编程错误,您的应用程序将终止。
一些指示:
长话短说:不要在 Swift 中简化错误处理。始终保持安全。
解决方法:如果您绝对必须捕获运行时错误,则必须使用进程边界来保护。运行另一个程序/进程并使用管道、套接字等进行通信。
面对无法抛出的方法抛出的异常。发现这个异常是从 API 的 Objective-c 部分抛出的。因此,您应该使用 Objective-c 以旧式方式捕获它。
首先创建objective-c 类,该类在init 方法中需要几个块——用于try、catch 和finally。
#import <Foundation/Foundation.h>
/**
Simple class for catching Objective-c-style exceptions
*/
@interface ObjcTry : NSObject
/**
* Initializeer
*
* @param tryBlock
* @param catchBlock
* @param finallyBlock
*
* @return object
*/
- (_Nonnull id)initWithTry:(nonnull void(^)(void))tryBlock catch:(nonnull void(^)( NSException * _Nonnull exception))catchBlock finally:(nullable void(^)(void))finallyBlock;
@end
在 .m 文件中:
#import "ObjcTry.h"
@implementation ObjcTry
- (_Nonnull id)initWithTry:(nonnull void(^)(void))tryBlock catch:(nonnull void(^)( NSException * _Nonnull exception))catchBlock finally:(nullable void(^)(void))finallyBlock
{
self = [super init];
if (self) {
@try {
tryBlock ? tryBlock() : nil;
}
@catch (NSException *exception) {
catchBlock ? catchBlock(exception) : nil;
}
@finally {
finallyBlock ? finallyBlock() : nil;
}
}
return self;
}
@end
其次,将其标头添加到 Bridging Header 文件中。
#import "ObjcTry.h"
并像这样在您的快速代码中使用它:
var list: [MyModel]!
_ = ObjcTry(withTry: {
// this method throws but not marked so, you cannot even catch this kind of exception using swift method.
if let items = NSKeyedUnarchiver.unarchiveObject(with: data) as? [MyModel] {
list = items
}
}, catch: { (exception: NSException) in
print("Could not deserialize models.")
}, finally: nil)
错误和异常之间是有区别的。Swift 只处理显式抛出的错误并且没有处理异常的本机能力。正如其他人所评论的那样,必须抛出错误,并且您无法捕捉未抛出的内容。
相比之下,Objective-C @try-@catch 处理异常,而不是错误。一些 objc 方法可能会导致异常,但不会以任何方式向编译器声明它们。例如 FileHandle.write。此类异常更接近于 Java 的 RuntimeException,后者也不需要声明。
在某些情况下,例如文件处理,在 Swift 中干净地处理异常会很好,并且可以通过使用 Objective-C 包装器来实现。请参阅http://stackoverflow.com/questions/34956002/how-to-properly-handle-nsfilehandle-exceptions-in-swift-2-0
此处转载的代码:
#ifndef ExceptionCatcher_h
#define ExceptionCatcher_h
#import <Foundation/Foundation.h>
NS_INLINE NSException * _Nullable tryBlock(void(^_Nonnull tryBlock)(void)) {
@try {
tryBlock();
}
@catch (NSException *exception) {
return exception;
}
return nil;
}
#endif /* ExceptionCatcher_h */
然后从 Swift 调用它:
let exception = tryBlock {
// execute dangerous code, e.g. write to a file handle
filehandle.write(data)
}
if exception != nil {
// deal with exception which is of type NSException
}
正如其他人提到的,你不应该捕获这些错误,你应该修复它们,但如果你想在程序终止之前执行更多代码,请NSSetUncaughtExceptionHandler
在函数AppDelegate
中使用。applicationdidFinishLaunchingWithOptions
功能说明:
更改顶级错误处理程序。
设置顶级错误处理函数,您可以在该函数中在程序终止前执行最后一分钟的日志记录。
你根本做不到。整个do-try-catch
ordo-catch
语句旨在用于捕获未处理的错误并...
我的意思是,如果首先没有发生错误,那么捕获错误是没有意义的……我看不出你为什么要这样做,你只会让编译器无缘无故地生气。
如果您安全地用if let
orguard let
语句打开可选的包装,这是相同的场景
guard let smth = smthOpt?.moreSpecific else { return }
//Compiler gives warning - unused variable smth. You wouldn't declare the variable and then not use it, or you would?
Simply Do-Catch 并不意味着用于安全使用,我看不出有任何理由在不处理需要捕获的风险操作时使用它......
如需进一步了解,请参阅: