Swift中的以下代码引发了NSInvalidArgumentException异常:
task = NSTask()
task.launchPath = "/SomeWrongPath"
task.launch()
如何捕捉异常?据我所知,Swift中的try / catch是针对Swift中抛出的错误,而不是针对像NSTask这样的对象引发的NSExceptions(我猜这是用ObjC编写的)。我是Swift的新手,所以我可能会错过一些明显的东西......
编辑:这里有一个针对该漏洞的雷达(专门针对NSTask):openradar.appspot.com/22837476
答案 0 :(得分:110)
以下是一些将NSExceptions转换为Swift 2错误的代码。
现在你可以使用
do {
try ObjC.catchException {
/* calls that might throw an NSException */
}
}
catch {
print("An error ocurred: \(error)")
}
ObjC.h:
#import <Foundation/Foundation.h>
@interface ObjC : NSObject
+ (BOOL)catchException:(void(^)())tryBlock error:(__autoreleasing NSError **)error;
@end
ObjC.m
#import "ObjC.h"
@implementation ObjC
+ (BOOL)catchException:(void(^)())tryBlock error:(__autoreleasing NSError **)error {
@try {
tryBlock();
return YES;
}
@catch (NSException *exception) {
*error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:exception.userInfo];
return NO;
}
}
@end
不要忘记将其添加到“* -Bridging-Header.h”中:
#import "ObjC.h"
答案 1 :(得分:12)
我建议创建一个C函数来捕获异常并返回NSError。然后,使用此功能。
该功能可能如下所示:
NSError *tryCatch(void(^tryBlock)(), NSError *(^convertNSException)(NSException *))
{
NSError *error = nil;
@try {
tryBlock();
}
@catch (NSException *exception) {
error = convertNSException(exception);
}
@finally {
return error;
}
}
有了一点桥接帮助,你只需要打电话:
if let error = tryCatch(task.launch, myConvertFunction) {
print("An exception happened!", error.localizedDescription)
// Do stuff
}
// Continue task
注意:我没有真正测试它,我找不到一个快速简便的方法在Playground中使用Objective-C和Swift。
答案 2 :(得分:9)
TL; DR:使用Carthage包含 https://github.com/eggheadgames/SwiftTryCatch 或CocoaPods以包含 https://github.com/ravero/SwiftTryCatch。
然后你可以使用这样的代码,而不用担心会崩溃你的应用程序:
import Foundation
import SwiftTryCatch
class SafeArchiver {
class func unarchiveObjectWithFile(filename: String) -> AnyObject? {
var data : AnyObject? = nil
if NSFileManager.defaultManager().fileExistsAtPath(filename) {
SwiftTryCatch.tryBlock({
data = NSKeyedUnarchiver.unarchiveObjectWithFile(filename)
}, catchBlock: { (error) in
Logger.logException("SafeArchiver.unarchiveObjectWithFile")
}, finallyBlock: {
})
}
return data
}
class func archiveRootObject(data: AnyObject, toFile : String) -> Bool {
var result: Bool = false
SwiftTryCatch.tryBlock({
result = NSKeyedArchiver.archiveRootObject(data, toFile: toFile)
}, catchBlock: { (error) in
Logger.logException("SafeArchiver.archiveRootObject")
}, finallyBlock: {
})
return result
}
}
@BPCorp接受的答案按预期工作,但正如我们所发现的,如果您尝试将此Objective C代码合并到大多数Swift框架中然后运行测试,那么事情会变得有趣。我们遇到了无法找到类函数的问题(错误:使用未解析的标识符)。因此,由于这个原因,以及一般的易用性,我们将其打包为一般用途的Carthage库。
奇怪的是,我们可以在其他地方使用Swift + ObjC框架,没有任何问题,只是框架的单元测试正在挣扎。
要求PR! (让它成为一个组合CocoaPod和迦太基的构建,并进行一些测试会很好。)
答案 3 :(得分:3)
如评论中所述,此API会针对其他可恢复的失败条件抛出异常,这是一个错误。 File it, and request an NSError-based alternative.大多数情况下,目前的状况是不合时宜的,因为NSTask
可以追溯到Apple标准化的例外仅针对程序员错误。
与此同时,当你可以使用其他答案中的一种机制来捕获ObjC中的异常并将它们传递给Swift时,请注意这样做并不是非常安全。 ObjC(和C ++)异常背后的堆栈展开机制是脆弱的,并且与ARC基本上不兼容。这就是为什么Apple仅为程序员错误使用异常的原因之一 - 这个想法是你可以(理论上至少)在开发过程中解决应用程序中的所有异常情况,并且在生产代码中不会出现异常。 (另一方面,Swift错误或NSError
可表示可恢复的情境或用户错误。)
更安全的解决方案是预见在调用API之前可能导致API抛出异常并处理它们的可能情况。如果您要编入NSArray
索引,请先检查其count
。如果您将launchPath
上的NSTask
设置为可能不存在或可能无法执行的内容,请在启动任务之前使用NSFileManager
进行检查。
答案 4 :(得分:1)
您无法在Swift中捕获Objective-C异常。但是,您可以通过制作一个Objective-C包装器来解决此问题,然后将其导入Swift。我已经完成了这项工作,并使其成为可重用的Swift Package Manager软件包。只需在Xcode中添加this package,然后像这样使用它即可:
import Foundation
import ExceptionCatcher
final class Foo: NSObject {}
do {
let value = try ExceptionCatcher.catch {
return Foo().value(forKey: "nope")
}
print("Value:", value)
} catch {
print("Error:", error.localizedDescription)
//=> Error: [valueForUndefinedKey:]: this class is not key value coding-compliant for the key nope.
}
答案 5 :(得分:0)
根据documentation中的说明,这是一种简单且轻巧的方法:
do {
try fileManager.moveItem(at: fromURL, to: toURL)
} catch let error as NSError {
print("Error: \(error.domain)")
}
答案 6 :(得分:0)
传输错误信息的版本稍好。
.Include(x => x.Stores)
用法示例:
var res = _db.Cities.Where(x => x.Id == id)
.Include(x => x.Parks) // this works fine
.FirstOrDefault();
之前:
@implementation ObjCExceptionHandler
+ (BOOL)tryExecute:(nonnull void(NS_NOESCAPE^)(void))tryBlock error:(__autoreleasing NSError * _Nullable * _Nullable)error {
@try {
tryBlock();
return YES;
}
@catch (NSException *exception) {
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
if (exception.userInfo != NULL) {
userInfo = [[NSMutableDictionary alloc] initWithDictionary:exception.userInfo];
}
if (exception.reason != nil) {
if (![userInfo.allKeys containsObject:NSLocalizedFailureReasonErrorKey]) {
[userInfo setObject:exception.reason forKey:NSLocalizedFailureReasonErrorKey];
}
}
*error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:userInfo];
return NO;
}
}
@end
之后:
let c = NSColor(calibratedWhite: 0.5, alpha: 1)
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
do {
try ObjC.perform {
c.getRed(&r, green: &g, blue: &b, alpha: &a)
}
} catch {
print(error)
}