繁体   English   中英

是否存在与 iOS 目标 C 的内部异常等效的情况?

[英]Is there an equivalent to an inner exception for iOS Objective C?

我想记录一些从我的 iOS 应用程序到 Firebase Crashlytics 的非致命错误。 我从我们正在使用的各种 SDK 中获取 NSError 对象; 但是,它们本身并不能提供非常丰富的信息。 我需要向它们添加一条消息,以便我可以知道它们在我们的代码中被调用的位置以及在错误条件发生之前发生了什么。 对于我们的 Java Android 应用程序,我可以创建一个带有内部异常的新异常,如下所示:

new Exception("My message", originalException);

iOS Objective C 中是否有等价物?

其他可能解决我的问题的事情:

  • 我在 Crashlytics 上只看到一个recordError方法,它只接受一个“错误”参数。 有没有我不知道的隐藏方法?
  • 我可以创建一个新的 NSError object 并克隆所有内容,在我的新消息之前。 这样做合理吗? 我需要复制原始 NSError object 的所有属性以确保捕获有关原始错误的所有重要信息?
  • 我可以在调用 recordError 之前调用recordError loglogWithFormat ,然后我想知道的任何历史记录都会显示在 Crashlytics 的 Log 选项卡上。 我对此并不感到疯狂,因为似乎代码中的多个点可能会发生相同的错误,而且我不一定希望 Firebase 将它们混为一谈。 Firebase 是否足够聪明,不会将它们混为一谈? 或者,这只是“事情的完成方式” ,我需要克服它并以这种方式添加我的遥测而不抱怨吗?

谢谢你。

我建议您从 Crashlytics 方面解决这个问题。 您需要首先确定您希望如何跟踪您的信息、您正在跟踪哪些信息以及您将如何跟踪它。 当您有诸如“Firebase 可以将它们放在一起吗?”之类的答案时您应该深入研究代码并选择合适的解决方案。 使用 Crashlytics 以外的其他工具也可能是一种解决方案(不是说你应该这样做,只是可以)。

我个人会 go 子类化NSError object 并包装原始文件。 您可以添加其他属性,以后可以帮助您跟踪信息。 从技术上讲,您的 class 可以包装您的 class 的另一个实例,该实例包装了一个NSError ,因此您可能会遇到error->originalError->originalError ,这是一件好事。 另一个可能发生的不方便的事情是您在不同的模块中发送错误两次或多次(原始+包装)。 您还可以通过一些仅创建一次的唯一标识符来跟踪它。

尝试深入研究一些代码......

@interface AppError: NSError

@property (nonatomic, strong) NSError *originalError;
@property (nonatomic, strong) NSString *message;
@property (nonatomic, strong) NSString *identifier;

@end

@implementation AppError

- (id)initWithMessage:(NSString *)message andOriginalError:(NSError *)error {
    if((self = [super initWithDomain:@"AppError" code:100 userInfo:nil])) {
        self.originalError = error;
        self.message = message;
        
        if([error isKindOfClass:[AppError class]]) {
            self.identifier = [(AppError *)error identifier];
        } else {
            self.identifier = [[NSUUID new] UUIDString];
        }
    }
    return self;
}

+ (NSError *)flatMapErrors:(NSError *)error {
    if(![error isKindOfClass:[AppError class]]) {
        return error; // Is a normal error so just return it
    }
    
    NSString *identifier = nil;
    NSMutableArray<NSString *> *messages = [[NSMutableArray<NSString *> alloc] init];
    NSError *originalError = nil;
    
    for(AppError *currentError = error; [currentError isKindOfClass:[AppError class]]; currentError = currentError.originalError) {
        identifier = currentError.identifier;
        originalError = currentError.originalError;
        [messages addObject:currentError.message];
    }
    
    NSString *message = nil;
    for(NSString *errorMessage in messages.reverseObjectEnumerator) {
        if(message == nil) {
            message = errorMessage;
        } else {
            message = [NSString stringWithFormat:@"%@ (%@)", errorMessage, message];
        }
    }
    
    return [NSError errorWithDomain:@"AppError.flatMapErrors" code:originalError.code userInfo:@{@"message": message, @"original_error": originalError, @"error_id": identifier}];
}

@end

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Case 1
    AppError *err = [[AppError alloc] initWithMessage:@"Test" andOriginalError:[NSError errorWithDomain:@"testCase1" code:500 userInfo:@{@"dev_message": @"something bad happened"}]];
    NSLog(@"%@", [AppError flatMapErrors:err]);
    // Case 2
    NSLog(@"%@", [AppError flatMapErrors:[self scenarioA]]);
}

- (NSError *)scenarioA {
    return [[AppError alloc] initWithMessage:@"Scenario A occurred" andOriginalError:[self scenarioB]];
}

- (NSError *)scenarioB {
    return [[AppError alloc] initWithMessage:@"Scenario B occurred" andOriginalError:[self scenarioC]];
}

- (NSError *)scenarioC {
    return [[AppError alloc] initWithMessage:@"Scenario C occurred" andOriginalError:[NSError errorWithDomain:@"testCase2" code:500 userInfo:@{@"dev_message": @"something bad happened"}]];
}



@end

(抱歉对ObjectiveC有点生疏)

无论如何,您都可以更改接口您的错误 class 实现。 但大多数情况下,这种方法可以将您想要的任意数量的错误打包在一起,这在第二种情况下得到了证明。 结果打印

Error Domain=AppError.flatMapErrors
Code=500 "(null)" 
UserInfo = { 
    message = Scenario A occurred (Scenario B occurred (Scenario C occurred)),          
    original_error=Error Domain=testCase2 Code=500 "(null)" UserInfo={dev_message=something bad happened}, 
    error_id=447149A0-F770-4F1F-90CA-0E7BF8967B1C
}

通过这种方式,您可以获得推送到 Crahlytics 等任何服务所需的所有信息。 从您的代码方面,您总是需要使用能够同时解决AppErrorNSErrorflatMapErrors ,这非常方便。 因此,您无需在任何地方更改界面。 请注意,方法scenarioN返回普通NSError而不是AppError

此外,添加唯一标识符将帮助您识别是否已多次发送错误。 想象一下方法scenarioBscenarioC都会向Crashlytics 发送错误。 该错误看起来会有所不同,但本质上是相同的错误。 您将能够在 Crashlytics 中看到这一点,因为您会看到 2 个具有相同标识符的错误。

代码需要一些改进,但我相信它应该足以说明这些功能。 希望它能让你更接近你的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM