从libjml错误处理程序回调函数(Obj-C ++)引发的Obj-C异常未在Obj-C中捕获

时间:2010-11-23 00:49:35

标签: objective-c libxml2 objective-c++

我在Obj-C MyStreamHandler.mm 类文件中创建了一个libxml错误处理程序,并使用xmlTextReaderSetErrorHandler将其分配给流解析器。在错误处理程序中,我使用[NSException raise:format:]引发Obj-C异常。但是,在我的Obj-C类的@try/@catch块中没有捕获异常。

在libxml中遇到无效的xml(例如ass< / foo< / bar>)时会触发回调。错误处理程序被调用很多是确定的,并且已知NSException也被引发。但是,我的Obj-C类(libxml的客户端)没有捕获异常。

MyStreamHandler.mm

#import <libxml/xmlreader.h>

#import "MyStreamHandler.h"

void MyErrorCallback(void *arg, const char *msg, xmlParserSeverities severity, xmlTextReaderLocatorPtr locator);

@implementation MyStreamHandler

-(void)readStream:(xmlTextReaderPtr)streamAPI
{
    xmlTextReaderSetErrorHandler(streamAPI, (xmlTextReaderErrorFunc)MyErrorCallback, NULL);

    @try
    {
        while (xmlTextReaderRead(streamAPI) == 1)
        {
            // Loop through the XML until the malformed XML is hit.
        }
    }
    @catch (id e)
    {
        NSLog(e);
    }
}

@end

void MyErrorCallback(void *arg, const char *msg, xmlParserSeverities severity, xmlTextReaderLocatorPtr locator)
{
   [NSException raise:@"MyException" format:@"Something bad happened: %s",msg];
}

这是一个示例XML文件,它将触发此回调和异常:

<foo><bar</foo>

以下是由未捕获的异常引起的关联调用堆栈:

*** Terminating app due to uncaught exception 'MyException', reason: 'Something bad happened: Opening and ending tag mismatch: content line 0 and container
'

*** Call stack at first throw:
(_NSCallStackArray*)0x10301a40; count=33 {
0   CoreFoundation                      0x04324be9 __exceptionPreprocess + 185
1   libobjc.A.dylib                     0x044795c2 objc_exception_throw + 47
2   CoreFoundation                      0x042dd628 +[NSException raise:format:arguments:] + 136
3   CoreFoundation                      0x042dd59a +[NSException raise:format:] + 58
4   MyApp                               0x00ee1b85 _Z25MyErrorCallbackPvPKc19xmlParserSeveritiesS_ + 319
5   libxml2.2.dylib                     0x0401766c xmlTextReaderStandalone + 105
6   libxml2.2.dylib                     0x04018793 xmlTextReaderSetErrorHandler + 838
7   libxml2.2.dylib                     0x03f69c78 __xmlRaiseError + 1222
8   libxml2.2.dylib                     0x03f6d564 namePush + 1283
9   libxml2.2.dylib                     0x03f75e2c xmlParseXMLDecl + 1291
10  libxml2.2.dylib                     0x03f80b6d xmlParseChunk + 3984
11  libxml2.2.dylib                     0x0401a255 xmlTextReaderGetAttribute + 816
12  libxml2.2.dylib                     0x0401bb34 xmlTextReaderRead + 441
13  MyApp                               0x00ec2919 +[MyStreamHandler readStream:]
...
Application call stack
...
24  CoreFoundation                      0x0429567d __invoking___ + 29
25  CoreFoundation                      0x04295551 -[NSInvocation invoke] + 145
26  Foundation                          0x027bd555 -[NSInvocationOperation main] + 51
27  Foundation                          0x0272bbd2 -[__NSOperationInternal start] + 747
28  Foundation                          0x0272b826 ____startOperations_block_invoke_2 + 106
29  libSystem.B.dylib                   0x925a0024 _dispatch_call_block_and_release + 16
30  libSystem.B.dylib                   0x925922f2 _dispatch_worker_thread2 + 228
31  libSystem.B.dylib                   0x92591d81 _pthread_wqthread + 390
32  libSystem.B.dylib                   0x92591bc6 start_wqthread + 30
}
terminate called after throwing an instance of 'NSException'

这完全是假的吗?我能从Obj-C ++函数中抛出一个Obj-C异常,并通过我的Obj-C代码中的普通@ try / @ catch块来处理它吗?或者我是否需要使用它自己的Obj-C ++ try / catch块来引发实际的Obj-C ++异常?

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

Cocoa和Cocoa Touch中的异常只保留给程序员错误;您不应期望在生产代码中将它们用于最终用户。

您通常也不能期望捕获跨API边界抛出的异常,仅在您控制所有中间堆栈帧的情况下。因此,在您给出的示例中,您将从没有任何Objective-C异常知识的代码中抛出异常,因此可能不会处于有效状态。

您必须让错误处理程序以其他方式将错误信息传递给更高级别的代码。这通常是为什么回调和回调注册API具有void *参数(例如xmlTextReaderSetErrorHandler的最终参数,它成为处理函数的第一个参数) - 它允许您将一些上下文与您的关联回调所以你可以做一些事情,比如确定你可能正在阅读的多个可能的文件中的哪一个已经触发了错误。

答案 1 :(得分:0)

我不是Apple对异常建议的忠实粉丝,因为我认为异常范例通常会导致更清晰的代码,因为您不必使用额外的NSError**参数来丢弃它而您不需要留出特殊的返回值来表示错误。

但是你绝对不能允许Objective-C异常在不期望它们的代码中传播。这是因为没有编写很多代码来处理Objective-C异常。例如,libxml2几乎肯定不会正确解开堆栈导致泄漏和其他恶意。

我不知道为什么你的处理程序没有抓住你的异常(也许你需要@throw它),但那是无关紧要的。在这种情况下你不能使用它们。

在回电中你应该做的只是注意你有错误,可能记录或记录它然后正常返回。如果错误是致命的,libxml2将知道如何在没有你帮助的情况下终止。