Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/120.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
iOS目标C是否存在与内部异常等价的异常?_Ios_Crashlytics_Telemetry - Fatal编程技术网

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

iOS目标C是否存在与内部异常等价的异常?,ios,crashlytics,telemetry,Ios,Crashlytics,Telemetry,我想将iOS应用程序中的一些非致命错误记录到Firebase Crashlytics。我从我们正在使用的各种SDK中获取N错误对象;然而,它们本身并没有提供太多的信息。我需要向它们添加一条消息,这样我就可以告诉它们在代码中的什么地方被调用,以及在错误条件发生之前发生了什么。对于我们的Java Android应用程序,我可以创建一个带有内部异常的新异常,如下所示: 新异常(“我的消息”,原始异常) iOS目标C是否有一个等价物 其他可能解决我问题的方法: 我在Crashlytics上只看到一个r

我想将iOS应用程序中的一些非致命错误记录到Firebase Crashlytics。我从我们正在使用的各种SDK中获取N错误对象;然而,它们本身并没有提供太多的信息。我需要向它们添加一条消息,这样我就可以告诉它们在代码中的什么地方被调用,以及在错误条件发生之前发生了什么。对于我们的Java Android应用程序,我可以创建一个带有内部异常的新异常,如下所示:

新异常(“我的消息”,原始异常)

iOS目标C是否有一个等价物

其他可能解决我问题的方法:

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

谢谢。

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

我个人会对一个
NSError
对象进行子类化,并包装原始文件。您可以添加其他属性,这些属性稍后可以帮助您跟踪信息。从技术上讲,您的类可以包装类的另一个实例,该实例包装了
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
通过这种方式,您可以获得推送到Crahlytics等服务所需的所有信息。在代码方面,您始终需要使用
flatMapErrors
,它能够解决
AppError
NSError
这两个问题,这非常方便。所以您不需要在任何地方更改接口。请注意,方法
scenarioN
返回普通
NSError
而不是
AppError

此外,添加唯一标识符将帮助您识别错误是否已发送多次。假设方法
scenarioB
scenarioC
都会向Crashlytics发送错误。错误看起来不同,但本质上是相同的错误。您可以在Crashlytics中看到这一点,就像您可以看到具有相同标识符的两个错误一样


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

您可以子类化
NSError
,并在子类中创建一个初始化器,该初始化器接受消息和
NSError
的实例,然后确保您的
localisedDescription
返回适当的字符串;说出你的信息,再加上另一条有意义的
NSError
localisedescription
。我只是有点恼火,语言中没有任何东西已经做到了这一点。我以为我错过了什么。
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
}