在Swift中捕获NSException

下面的代码在Swift引发NSInvalidArgumentExceptionexception:

task = NSTask() task.launchPath = "/SomeWrongPath" task.launch() 

我怎样才能抓住例外? 据我所知,在Swift中尝试/ catch是针对Swift中引发的错误,而不是从像NSTask(我猜是用ObjC编写的)对象引发的NSExceptions。 我是新来的Swift,所以可能是我失去了一些明显的东西…

编辑 :这里是一个错误的雷达(专为NSTask): openradar.appspot.com/22837476

这是一些代码,将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" 

我build议做一个C函数,将捕获exception,并返回一个NSError。 然后,使用这个function。

该函数可能看起来像这样:

 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 

注意:我没有真正testing它,我找不到一个快速简单的方法在游乐场中使用Objective-C和Swift。

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框架中,然后运行testing,情况就会变得有趣。 我们遇到了类函数没有find的问题( 错误:使用未parsing的标识符 )。 所以,出于这个原因,只是一般的易用性,我们把它打包成一个通用的Carthage库。

奇怪的是,我们可以在其他地方使用Swift + ObjC框架而没有任何问题,这只是框架的unit testing而已。

PRs请求! (这将是一个很好的组合CocoaPod和Carthage的build设,以及有一些testing)。

正如评论中指出的,这个API抛出exception,否则可恢复的失败条件是一个错误。 文件,并请求基于NSError的替代。 大多数情况下,现在的情况是不符合时代要求的,因为NSTaskdate要早于苹果公司规定只有程序员错误的例外。

同时,虽然您可以使用其他答案的机制之一来捕获ObjC中的exception,并将它们传递给Swift,但请注意,这样做不是很安全。 ObjC(和C ++)exception后面的堆栈展开机制很脆弱,基本上与ARC不兼容。 这就是为什么苹果只为程序员错误使用exception的原因之一 – 理念是在开发过程中,您可以(理论上至less)在您的应用程序中理清所有exception情况,并且在您的生产代码中不会出现exception情况。 (另一方面,Swift错误或NSError可以指示可恢复的情境或用户错误。)

更安全的解决scheme是预见可能导致API在调用API之前抛出exception并处理它们的可能条件。 如果你正在索引一个NSArray ,请先检查它的count 。 如果您将launchPath上的NSTask设置为可能不存在或可能不可执行的内容,请在启动任务之前使用NSFileManager进行检查。