在Swift中捕获NSException
Swift中的以下代码引发NSInvalidArgumentException异常:在Swift中捕获NSException,swift,cocoa-touch,exception,foundation,nsexception,Swift,Cocoa Touch,Exception,Foundation,Nsexception,Swift中的以下代码引发NSInvalidArgumentException异常: task = NSTask() task.launchPath = "/SomeWrongPath" task.launch() 如何捕获异常?据我所知,Swift中的try/catch是针对Swift中抛出的错误,而不是针对像NSTask这样的对象引发的nsException(我猜这是用ObjC编写的)。我是斯威夫特的新手,所以可能我错过了一些明显的东西 编辑:这里有一个bug雷达(专门针对NSTask):
task = NSTask()
task.launchPath = "/SomeWrongPath"
task.launch()
如何捕获异常?据我所知,Swift中的try/catch是针对Swift中抛出的错误,而不是针对像NSTask这样的对象引发的nsException(我猜这是用ObjC编写的)。我是斯威夫特的新手,所以可能我错过了一些明显的东西
编辑:这里有一个bug雷达(专门针对NSTask):我建议使用一个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
注意:我并没有真正测试它,我找不到一种快速简便的方法在操场上使用Objective-C和Swift;DR:使用迦太基包含,或使用椰子荚包含 然后,您可以使用这样的代码,而不用担心它会使您的应用程序崩溃:
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所接受的答案按预期工作,但我们发现,如果您尝试将此目标C代码合并到大多数Swift框架中,然后运行测试,事情会变得有点有趣。找不到类函数时出现问题(错误:使用未解析的标识符)。因此,出于这个原因,以及一般的易用性,我们将其打包为一个通用的迦太基图书馆
奇怪的是,我们可以在其他地方使用Swift+ObjC框架而没有任何问题,只是框架的单元测试遇到了困难
请求PRs!(最好有CocoaPod和Carthage的组合构建,并进行一些测试)。如评论中所述,该API为其他可恢复的故障条件抛出异常是一个bug。大多数情况下,目前的状况是一个时代错误,因为
NSTask
可以追溯到苹果标准化之前,只有程序员错误才有例外
同时,虽然您可以使用其他答案中的一种机制来捕获ObjC中的异常并将其传递给Swift,但请注意这样做并不十分安全。ObjC(和C++)异常背后的堆栈展开机制是脆弱的,从根本上与ARC不兼容。这就是为什么苹果只对程序员错误使用异常的部分原因——其想法是,你可以(至少理论上)在开发过程中整理出应用程序中的所有异常情况,并且在生产代码中不会出现异常。(另一方面,Swift错误或n错误
s可指示可恢复的情景或用户错误。)
更安全的解决方案是预见可能导致API抛出异常的情况,并在调用API之前进行处理。如果要索引到
NSArray
,请首先检查其计数。如果要将NSTask上的启动路径设置为可能不存在或不可执行的内容,请在启动任务之前使用NSFileManager
进行检查。以下是一些将NSException转换为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(^)(void))tryBlock error:(__autoreleasing NSError **)error;
@end
不要忘记将其添加到“*-桥接头.h”:
如中所述,这是一种简单、轻量级的方法:
do{
尝试fileManager.moveItem(位于:fromURL,to:toURL)
}将let错误捕获为NSError{
打印(“错误:\(Error.domain)”)
}
您无法在Swift中捕获Objective-C异常。然而,您可以通过制作Objective-C包装器来解决这个问题,然后将其导入Swift。我已经完成了这项工作,并使其成为一个可重用的Swift包管理器包。只需添加Xcode,然后像这样使用它:
NSError *tryCatch(void(^tryBlock)(), NSError *(^convertNSException)(NSException *))
{
NSError *error = nil;
@try {
tryBlock();
}
@catch (NSException *exception) {
error = convertNSException(exception);
}
@finally {
return error;
}
}
<代码>导入基础
导入例外跟踪程序
最后一个类Foo:NSObject{}
做{
let value=try ExceptionCatcher.catch{
返回Foo().value(forKey:“nope”)
}
打印(“值:”,值)
}抓住{
打印(“错误:,错误。本地化描述)
//=>错误:[valueForUndefinedKey:]:此类不符合密钥nope的键值编码。
}
该版本改进了上述答案,以返回实际的详细异常消息
@implementation ObjC
+(BOOL)tryExecute:(非null void(NS_NOESCAPE^)(void))tryBlock错误:(uu自动删除NSError*_Nullable*_Nullable)错误{
@试一试{
tryBlock();
返回YES;
}
@捕获(NSException*异常){
NSMutableDictionary*userInfo=[[NSMutableDictionary alloc]init];
if(exception.userInfo!=NULL){
userInfo=[[NSMutableDictionary alloc]initWithDictionary:exception.userInfo];
}
if(exception.reason!=nil){
如果(![userInfo.allKeys包含对象:NSLocalizedFailureReasonErrorKey]){
[userInfo setObject:exception.reason forKey:NSLocalizedFailureReasonErrorKey];
}
}
*错误=[[NSError alloc]initWithDomain:exception.name代码:0 userInfo:userInfo];
返回否;
}
}
@结束
用法示例:
设c=NSColor(校准白色:0.5,α:1)
变量r:CGFloat=0
变量g:CGFloat=0
变量b:CGFloat=0
变量a:CGFloat=0
做{
请尝试ObjC.tryExecute{
c、 红色(&r,绿色:&g,蓝色:&b,阿尔法:&a)
}
}抓住{
打印(错误)
}
之前:
Error Domain=NSInvalidArgumentException Code=0 "(null)"
之后:
Error Domain=NSInvalidArgumentException Code=0
"*** -getRed:green:blue:alpha: not valid for the NSColor NSCalibratedWhiteColorSpace 0.5 1; need to first convert colorspace."
UserInfo={NSLocalizedFailureReason=*** -getRed:green:blue:alpha: not valid for the NSColor NSCalibratedWhiteColorSpace 0.5 1; need to first convert colorspace.}
不幸的是,您无法在Swift中捕获Objective-C异常,例如,请参见。有人可能会认为NSTASK抛出异常而不是返回错误,你可以提交bug报告,但是我怀疑苹果会改变API。谢谢@ MartinR。我认为这要么是API中的一个bug,要么Swift应该提供一种捕获ObjC异常的机制(或者两者更好)。。。无论如何,我打开了一个bug(),不过我想还有更多的API方法存在同样的问题。我的例子是NSPredicate(fromtMetadataQueryString:)
。这应该是一个init?
,因此如果字符串是b
Error Domain=NSInvalidArgumentException Code=0
"*** -getRed:green:blue:alpha: not valid for the NSColor NSCalibratedWhiteColorSpace 0.5 1; need to first convert colorspace."
UserInfo={NSLocalizedFailureReason=*** -getRed:green:blue:alpha: not valid for the NSColor NSCalibratedWhiteColorSpace 0.5 1; need to first convert colorspace.}