스위프트에서 NS Exception 포착
Swift의 다음 코드는 NSIvalidArgument를 제기합니다.예외 예외:
task = NSTask()
task.launchPath = "/SomeWrongPath"
task.launch()
예외를 어떻게 잡을 수 있습니까?Swift에서 try/catch는 Swift 내에서 발생한 오류에 대한 것이지 NSTask(ObjC에 기록되어 있음)와 같은 객체에서 발생한 NS 예외에 대한 것은 아닌 것으로 알고 있습니다.스위프트가 처음이라 뭔가를 놓쳤나봐요
편집: 여기 버그에 대한 레이더(NSTask용 specif): openradar.appspot.com/22837476
NS Exceptions를 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;
}
}
그리고 가교 역할을 조금만 해주면 다음과 같은 전화를 걸 수 있습니다.
if let error = tryCatch(task.launch, myConvertFunction) {
print("An exception happened!", error.localizedDescription)
// Do stuff
}
// Continue task
참고: 실제로 테스트하지는 않았지만 Objective-C와 Swift를 플레이그라운드에서 사용할 수 있는 빠르고 쉬운 방법을 찾을 수 없었습니다.
TL;DR: https://github.com/eggheadgames/SwiftTryCatch 을 포함하려면 카르타고를 사용하고 https://github.com/williamFalcon/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에서 수용된 답변은 의도대로 작동하지만, 우리가 발견한 바와 같이 이 목표 C 코드를 다수의 스위프트 프레임워크에 통합하고 테스트를 실행하면 상황이 조금 흥미로워집니다.클래스 함수를 찾을 수 없는 문제가 발생했습니다(오류: 확인되지 않은 식별자 사용).그래서, 그러한 이유로, 그리고 단지 일반적인 사용 편의성을 위해, 우리는 그것을 일반적인 용도의 카르타고 도서관으로 포장했습니다.
이상하게도 Swift + ObjC 프레임워크를 다른 곳에서 문제없이 사용할 수 있었는데, 프레임워크의 유닛 테스트만 어려워졌습니다.
PR이 요청했습니다! (코코아팟과 카르타고의 콤보 빌드와 함께 테스트도 해보는 것이 좋을 것 같습니다.)
주석에서 언급했듯이, 이 API가 복구 가능한 장애 조건에 대한 예외를 제공한다는 것은 버그입니다.파일을 작성하고 NSerror 기반 대안을 요청합니다.대부분의 현재 상황은 시대착오적입니다.NSTask
애플이 프로그래머 오류에 대해서만 예외를 두는 것을 표준화하기 전으로 거슬러 올라갑니다.
한편, 다른 답변의 메커니즘 중 하나를 사용하여 ObjC에서 예외를 포착하여 Swift에게 전달할 수는 있지만, 그렇게 하는 것은 그다지 안전하지 않습니다.ObjC(및 C++) 예외 뒤에 있는 스택 해제 메커니즘은 깨지기 쉽고 근본적으로 ARC와 호환되지 않습니다.이것은 애플이 프로그래머 오류에 대해서만 예외를 사용하는 이유의 일부입니다. 즉, 개발 중에 앱에서 (이론적으로는, 적어도) 모든 예외 사례를 분류할 수 있고, 생산 코드에서 예외가 발생하지 않는다는 것입니다.(Swift errors 또는NSError
s. 반면 복구 가능한 상황 또는 사용자 오류를 나타낼 수 있습니다.)
더 안전한 해결책은 API를 호출하기 전에 API가 예외를 던지고 처리하도록 할 수 있는 가능한 조건을 예측하는 것입니다.색인을 작성하는 경우NSArray
, 그것을 확인합니다.count
먼저. 만약에 당신이 그 일을 하고 있다면.launchPath
에 의거하여NSTask
존재하지 않거나 실행 가능하지 않을 수도 있는 것에 사용합니다.NSFileManager
작업을 시작하기 전에 확인할 수 있습니다.
버전은 위의 답변을 개선하여 실제 상세 예외 메시지를 반환합니다.
@implementation ObjC
+ (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.tryExecute {
c.getRed(&r, green: &g, blue: &b, alpha: &a)
}
} catch {
print(error)
}
이전:
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 예외를 잡을 수 없습니다.그러나 Objective-C 래퍼를 만들어서 Swift로 가져올 수 있습니다.저는 그 일을 했고 재사용 가능한 스위프트 패키지 매니저 패키지로 만들었습니다.이 패키지를 Xcode에 추가한 후 다음과 같이 사용하면 됩니다.
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.
}
언급URL : https://stackoverflow.com/questions/32758811/catching-nsexception-in-swift
'programing' 카테고리의 다른 글
정확한 문자열 일치를 위해 LIKE 대 = 사용 (0) | 2023.10.23 |
---|---|
태그의 onclick 속성에서 비롯된 사용자 지정 기능에서 기본값을 호출하지 못하도록 하는 액세스 이벤트 (0) | 2023.10.23 |
C++로 아두이노를 프로그래밍하는 방법 (0) | 2023.10.23 |
함수 매개변수의 세미콜론 (0) | 2023.10.23 |
리눅스: sig_atomic_type이 int로 지정되는 이유는 무엇입니까? (0) | 2023.10.23 |