问题
How to handle errors for methods or code that does not explicitly throw?
Wrapping it a do / catch block results in a compiler warning:
"'catch' block is unreachable because no errors are thrown in 'do' block"
Coming from C# / JAVA background this is an oddity to say the least. As a developer I should be able to safeguard and wrap any code block in do/catch block. Just because a method is not explicitly marked with "throw" does not mean that errors will not occur.
回答1:
Faced with an exception thrown from a method which cannot throw. Found out this exception was thrown from objective-c part of API. So you should catch it in old style way using objective-c.
Firstly create objective-c class which takes several blocks in init method - for try, catch and 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
In .m file:
#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
Second, add its headers to the Bridging Header file.
#import "ObjcTry.h"
And use it in your swift code like that:
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)
回答2:
What you are asking is not possible in Swift, as Swift has no facility to handle runtime errors such as out-of-bounds, access violations or failed forced unwrapping during runtime. Your application will terminate if any of these serious programming errors will occur.
Some pointers:
- Q: How to handle EXC_BAD_ACCESS? A: Not possible
- Q: How to handle failed forced unwrap? A: Not possible
- Q: How to handle array out of bounds? A: Not possible
Long story short: Don't short-cut error handling in Swift. Play it safe, always.
Workaround: If you absolutely must catch runtime-errors, you must use process boundaries to guard. Run another program/process and communicate using pipes, sockets, etc.
回答3:
I suspect that you'd like to catch errors which is not explicitly marked with "throws".
This makes no sense. You cannot catch other than errors which is explicitly marked with "throws". So, this warning is valid.
For this example, if executed, fatal error: Index out of range
will occur.
This is runtime error, and you cannot catch it.
For this example, you should check elements size like this, instead of doing try-catch error handling:
回答4:
There is a difference between ERRORS and EXCEPTIONS. Swift deals only with errors which are explicitly THROWN and has no native capability for dealing with EXCEPTIONS. As others have commented ERRORS must be thrown and you can't catch what isn't thrown.
By contrast Objective-C @try-@catch deals with exceptions, not errors,. Some objc methods may cause exceptions but do not declare them in any way to the compiler. e.g. FileHandle.write. Such exceptions are more closely aligned to Java's RuntimeException which also does not need to be declared.
There are some situations such as file handling where it would be nice to handle exceptions cleanly in Swift and it is possible by using an Objective-C wrapper. See http://stackoverflow.com/questions/34956002/how-to-properly-handle-nsfilehandle-exceptions-in-swift-2-0
Code reproduced here:
#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 */
Then calling it from 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
}
回答5:
As others mentioned, you should not catch these errors, you should fix them, but in case you want to execute more code before the program terminates, use NSSetUncaughtExceptionHandler
in AppDelegate
in applicationdidFinishLaunchingWithOptions
function.
The function description:
Changes the top-level error handler.
Sets the top-level error-handling function where you can perform last-minute logging before the program terminates.
回答6:
You simply cannot. The whole do-try-catch
or do-catch
statement is meant to be used to catch unhandled errors and...
I mean there is no point in catching error if no error occurs in first place... I see no scenario why would you want to do such thing, you make only compiler angry for no reason.
It's the same scenario if you safely unwrap optional with if let
or guard let
statements
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 is not meant to be used for safe use and I don't see any reason why to use it when not dealing with risky operations which need to catch...
for further understanding, see:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html
来源:https://stackoverflow.com/questions/41994538/swift-error-handling-for-methods-that-do-not-throw