22

如何处理未明确抛出的方法或代码的错误?

将其包装为 do / catch 块会导致编译器警告:

"'catch' block is unreachable because no errors are thrown in 'do' block"

来自 C# / JAVA 背景,这至少可以说是一件奇怪的事情。作为一名开发人员,我应该能够在 do/catch 块中保护和包装任何代码块。仅仅因为一个方法没有明确地用“throw”标记并不意味着不会发生错误。

4

6 回答 6

9

您所问的问题在 Swift 中是不可能的,因为 Swift 无法处理运行时错误,例如越界、访问冲突或运行时强制解包失败。如果发生任何这些严重的编程错误,您的应用程序将终止。

一些指示:

长话短说:不要在 Swift 中简化错误处理。始终保持安全。

解决方法:如果您绝对必须捕获运行时错误,则必须使用进程边界来保护。运行另一个程序/进程并使用管道、套接字等进行通信。

于 2017-07-23T20:03:17.557 回答
8

我怀疑您想捕获未明确标记为“抛出”的错误。

在此处输入图像描述

这是没有意义的。除了明确标记为“抛出”的错误之外,您无法捕获其他错误。所以,这个警告是有效的。

对于这个例子,如果执行,fatal error: Index out of range将会发生。这是运行时错误,您无法捕捉到它。

对于此示例,您应该像这样检查元素大小,而不是执行 try-catch 错误处理:

在此处输入图像描述

于 2017-07-21T07:50:47.687 回答
7

面对无法抛出的方法抛出的异常。发现这个异常是从 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)
于 2017-07-21T08:40:40.620 回答
6

错误和异常之间是有区别的。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
}
于 2017-07-19T05:39:13.813 回答
1

正如其他人提到的,你不应该捕获这些错误,你应该修复它们,但如果你想在程序终止之前执行更多代码,请NSSetUncaughtExceptionHandler在函数AppDelegate中使用。applicationdidFinishLaunchingWithOptions

功能说明:

更改顶级错误处理程序。

设置顶级错误处理函数,您可以在该函数中在程序终止前执行最后一分钟的日志记录。

于 2017-07-24T10:21:46.363 回答
-1

你根本做不到。整个do-try-catchordo-catch语句旨在用于捕获未处理的错误并...

我的意思是,如果首先没有发生错误,那么捕获错误是没有意义的……我看不出你为什么要这样做,你只会让编译器无缘无故地生气。

如果您安全地用if letorguard 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 并不意味着用于安全使用,我看不出有任何理由在不处理需要捕获的风险操作时使用它......

如需进一步了解,请参阅:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html

于 2017-07-24T14:27:27.167 回答