Swift에서 NSException 잡기
Swift의 다음 코드는 NSInvalidArgumentException 예외를 발생시킵니다.
task = NSTask()
task.launchPath = "/SomeWrongPath"
task.launch()
예외를 어떻게 잡을 수 있습니까? 내가 이해하는 바와 같이 Swift의 try / catch는 NSTask와 같은 객체에서 발생한 NSExceptions가 아니라 Swift 내에서 발생한 오류를위한 것입니다 (ObjC로 작성되었습니다). 저는 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(^)(void))tryBlock error:(__autoreleasing NSError **)error;
@end
ObjC.m
#import "ObjC.h"
@implementation ObjC
+ (BOOL)catchException:(void(^)(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"
내가 제안하는 것은 예외를 포착하고 대신 NSError를 반환하는 C 함수를 만드는 것입니다. 그런 다음이 기능을 사용합니다.
함수는 다음과 같습니다.
NSError *tryCatch(void(^tryBlock)(), NSError *(^convertNSException)(NSException *))
{
NSError *error = nil;
@try {
tryBlock();
}
@catch (NSException *exception) {
error = convertNSException(exception);
}
@finally {
return error;
}
}
And with a little bridging help, you'll just have to call:
if let error = tryCatch(task.launch, myConvertFunction) {
print("An exception happened!", error.localizedDescription)
// Do stuff
}
// Continue task
Note: I didn't really test it, I couldn't find a quick and easy way to have Objective-C and Swift in a Playground.
TL;DR: Use Carthage to include https://github.com/eggheadgames/SwiftTryCatch or CocoaPods to include https://github.com/ravero/SwiftTryCatch.
Then you can use code like this without fear it will crash your app:
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
}
}
The accepted answer by @BPCorp works as intended, but as we discovered, things get a little interesting if you try to incorporate this Objective C code into a majority Swift framework and then run tests. We had problems with the class function not being found (Error: Use of unresolved identifier). So, for that reason, and just general ease of use, we packaged it up as a Carthage library for general use.
Strangely, we could use the Swift + ObjC framework elsewhere with no problems, it was just the unit tests for the framework that were struggling.
PRs requested! (It would be nice to have it a combo CocoaPod & Carthage build, as well as have some tests).
As noted in comments, that this API throws exceptions for otherwise-recoverable failure conditions is a bug. File it, and request an NSError-based alternative. Mostly the current state of affairs is an anachronism, as NSTask
dates to back before Apple standardized on having exceptions be for programmer errors only.
In the meantime, while you could use one of the mechanisms from other answers to catch exceptions in ObjC and pass them to Swift, be aware that doing so isn't very safe. The stack-unwinding mechanism behind ObjC (and C++) exceptions is fragile and fundamentally incompatible with ARC. This is part of why Apple uses exceptions only for programmer errors — the idea being that you can (theoretically, at least) sort out all the exception cases in your app during development, and have no exceptions occurring in your production code. (Swift errors or NSError
s, on the other hand, can indicate recoverable situational or user errors.)
The safer solution is to foresee the likely conditions that could cause an API to throw exceptions and handle them before calling the API. If you're indexing into an NSArray
, check its count
first. If you're setting the launchPath
on an NSTask
to something that might not exist or might not be executable, use NSFileManager
to check that before you launch the task.
As specified in the documentation, this is an easy and lightweight way to do it:
do {
try fileManager.moveItem(at: fromURL, to: toURL)
} catch let error as NSError {
print("Error: \(error.domain)")
}
참고URL : https://stackoverflow.com/questions/32758811/catching-nsexception-in-swift
'program story' 카테고리의 다른 글
빠른. (0) | 2020.11.20 |
---|---|
서비스 의도는 명시 적이어야 함 : 의도 (0) | 2020.11.20 |
SDK 플랫폼 도구 버전 ((23))이 너무 오래되어 API 23으로 컴파일 된 API를 확인할 수 없습니다. (0) | 2020.11.20 |
다른 파일에서 클래스 가져 오기 (0) | 2020.11.20 |
PHP (> = 5.0)에서 참조로 전달하는 것이 더 빠릅니까? (0) | 2020.11.20 |