【问题标题】:XPC and Exception handlingXPC 和异常处理
【发布时间】:2019-03-03 09:22:49
【问题描述】:

我有一个 LaunchAgent 使用 HockeyApp 进行崩溃报告。现在我注意到 HockeyApp 没有报告未捕获的异常,就像在普通的 macOS 应用程序中一样。

例如:

- (void)upperCaseString:(NSString *)aString withReply:(void (^)(NSString *))reply {
    NSArray *array = [NSArray array];
    reply([array objectAtIndex:23]);
}

从未到达NSUncaughtExceptionHandler,但控制台记录:

<NSXPCConnection: 0x7fe97dc0f110> connection from pid 44573: Warning: Exception caught during invocation of received message, dropping incoming message and invalidating the connection.

问题是如何使用 HockeyApp 报告未处理的异常。

【问题讨论】:

    标签: objective-c cocoa cocoa-touch xpc nsxpcconnection


    【解决方案1】:

    问题:

    XPC 似乎有自己的@try @catch 块,它在方法中捕获未处理的异常,记录异常,然后调用-[NSXPCConnection interruptionHandler]

    此问题已通过rdar://48543049 报告给 Apple。

    注意:这些是不是复制和过去的解决方案,请仔细评估您的崩溃报告框架。我链接到 PLCrashReporter 的实现细节。

    解决方案 A:

    @try @catch 块

    - (void)upperCaseString:(NSString *)aString withReply:(void (^)(NSString *))reply {
        @try {
            NSArray *array = [NSArray array];
            reply([array objectAtIndex:23]);
        } @catch (NSException *exception) {
            NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
            if (handler) {
                handler(exception);
            }
        }
    }
    

    讨论

    HockeyApp 使用 PLCrashReporter 进行崩溃报告。 PLCrashReporter 注册了一个NSUncaughtExceptionHandler (code)。所以上面的代码会将异常转发给 PLCrashReporter 异常处理程序并终止 (code) XPC。

    Mattie 建议再次@throw 异常,以触发内部 XPC @catch 块以及可能的内部清理和日志记录。 这是需要考虑的事情。特别是如果您在连接的 LaunchAgent/Server 端的 NSXPCConnection 上有一个自定义的 interruptionHandler

    现在我不会再扔它了,因为 我的 XPC 是完全无状态的,崩溃应该没问题。

    解决方案 A 的缺点是通过 XPC 公开的每个方法都需要这个 @try @catch 块

    解决方案 B:

    使用捕获所有未处理异常的NSProxyNSXPCConnection exportObject

    @interface LOUncaughtExceptionHandlerProxy : NSProxy {
        NSObject *_object;
    }
    @end
    
    @implementation LOUncaughtExceptionHandlerProxy
    
    - (instancetype)initWithObject:(NSObject *)object
    {
        NSParameterAssert(object);
        _object = object;
        return self;
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
    {
        return [_object methodSignatureForSelector:selector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation
    {
        @try {
            [invocation invokeWithTarget:_object];
        } @catch (NSException *exception) {
            NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
            if (handler) {
                handler(exception);
            }
        }
    }
    
    @end
    

    NSXPCListener 监听器中设置:

    - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
        XPC *exportedObject = [XPC new];
        LOUncaughtExceptionHandlerProxy *proxy = [[LOUncaughtExceptionHandlerProxy alloc] initWithObject:exportedObject];
        newConnection.exportedObject = proxy;
        newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
        [newConnection resume];
        return YES;
    }
    

    解决方案 A 的所有详细信息都适用于解决方案 B

    解决方案 Z:

    在 macOS 上可以使用 ExceptionHandling.framework,它的问题在BITCrashExceptionApplication.h 中有很好的概述。

    讨论

    如果框架未移植到 iOS,这绝不是一个好兆头。另请注意Matties 评论:

    我与 Apple 的互动直接表明 ExceptionHandling.framework 不再受支持。而且,根据我在 Crashlytics 上工作的经验,它存在一些基本的互操作性问题,超出了引用 header 中所指出的问题。

    【讨论】:

    • 方案1很棒,方案2很聪明!但是,我会考虑在 @catch 块的末尾添加一个 @throw 语句。现在,您正在更改运行时行为——尽管这可能是您的意图。另外:我与 Apple 的交互直接表明 ExceptionHandling.framework 不再受支持。而且,根据我在 Crashlytics 上工作的经验,它存在一些基本的互操作性问题,超出了引用的标题中指出的问题。不过,恐怕我没有记录这些细节。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 2017-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-07
    • 2012-09-15
    相关资源
    最近更新 更多