Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Swift中捕获NSException_Swift_Cocoa Touch_Exception_Foundation_Nsexception - Fatal编程技术网

在Swift中捕获NSException

在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):

Swift中的以下代码引发NSInvalidArgumentException异常:

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.}