我有一个使用HockeyApp的LaunchAgent进行崩溃报告。现在我注意到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报告未处理的异常。
XPC似乎有自己的@try @catch块,它捕获方法中的未处理异常,记录异常,然后调用-[NSXPCConnection interruptionHandler]
。
这个问题是根据rdar://48543049向Apple报告的。
注意:这些不是复制和过去的解决方案,请仔细评估您的崩溃报告框架。我链接到PLCrashReporter的实现细节。
@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端的interruptionHandler
上有一个自定义的NSXPCConnection
!
现在我一边不再扔它,因为我的XPC是完全无状态的,应该很好,只是崩溃。
解决方案A的缺点是通过XPC公开的每个方法都需要@try @catch块。
使用NSProxy
捕获所有未处理的异常,如NSXPCConnection 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.
在macOS上可以使用ExceptionHandling.framework,它的问题在BITCrashExceptionApplication.h中有很好的概述。
讨论
当框架未移植到iOS时,这绝不是一个好兆头。另请注意Matties评论:
我与Apple进行了交互,直接表明不再支持ExceptionHandling.framework。而且,根据我在Crashlytics工作期间的经验,它有一些基本的互操作性问题,超出了引用的header所指出的内容。