【问题标题】:Obj-C exception raised from libxml error handler callback function (Obj-C++) is not caught in Obj-C从 libxml 错误处理程序回调函数 (Obj-C++) 引发的 Obj-C 异常未在 Obj-C 中捕获
【发布时间】:2010-11-23 00:49:34
【问题描述】:

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

在libxml中遇到无效xml(如)时触发回调。错误处理程序的调用是肯定的,并且NSException 也已知会被引发。但是,在作为 libxml 客户端的 Obj-C 类中没有捕获异常。

MyStreamHandler.mm

#import 

#import "MyStreamHandler.h"

void MyErrorCallback(void *arg, const char *msg, xmlParserSeverities 严重性, xmlTextReaderLocatorPtr 定位器);

@implementation MyStreamHandler

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

    @尝试
    {
        而(xmlTextReaderRead(streamAPI)== 1)
        {
            // 循环遍历 XML,直到命中格式错误的 XML。
        }
    }
    @catch (id e)
    {
        NSLog(e);
    }
}

@结尾

void MyErrorCallback(void *arg,const char *msg,xmlParserSeverities 严重性,xmlTextReaderLocatorPtr 定位器)
{
   [NSException raise:@"MyException" format:@"发生了一些不好的事情:%s",msg];
}

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

下面是由 uncaught 异常引起的相关调用堆栈:

*** 由于未捕获的异常“MyException”而终止应用程序,原因:“发生了一些不好的事情:开始和结束标记不匹配:内容行 0 和容器
'

*** 第一次抛出调用堆栈:
(_NSCallStackArray*)0x10301a40;计数=33 {
0 核心基础 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 名称推送 + 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 读取流:]
...
应用程序调用堆栈
...
24 核心基础 0x0429567d __invoking___ + 29
25 CoreFoundation 0x04295551 - [NSInvocation 调用] + 145
26 基础 0x027bd555 -[NSInvocationOperation main] + 51
27 基础 0x0272bbd2 -[__NSOperationInternal start] + 747
28 基础 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
}
在抛出“NSException”实例后调用终止

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

任何帮助将不胜感激。

【问题讨论】:

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


    【解决方案1】:

    Cocoa 和 Cocoa Touch 中的异常仅供程序员错误使用;您不应该期望在交付给最终用户的生产代码中使用它们。

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

    您必须让错误处理程序以其他方式将错误信息传递给更高级别的代码。这就是为什么回调和回调注册 API 有一个 void * 参数(例如 xmlTextReaderSetErrorHandler 的最后一个参数,它成为处理函数的第一个参数) - 它允许您将一些上下文与您的回调相关联,以便您可以执行一些操作,例如确定您可能正在阅读的多个可能文档中的哪一个触发了错误。

    【讨论】:

    • 我们的模式是在文件阅读器中使用异常,然后在将控制权交还给应用程序之前捕获它们。然后我们将它们转换为 NSError** 并从那时起使用它。感谢克里斯的指导。
    • 您不应该使用该模式,无论您可能习惯于 Java 或 .NET 平台的什么。您不能只从任意 C 代码中抛出异常并在 Objective-C 或 C++ 中捕获它; C 代码使用的状态可能不一致,导致崩溃、挂起、安全漏洞等。
    • 这不是任意的 C 代码,我们的状态不会不一致。这也不适用于 Java 或 .NET 平台。这是已经发布了近十年的 Objective-C 和 Obj-C++ 代码。我们也已经完成了必要的模糊测试。
    • 通过“任意 C 代码”我指的是 libxml2、SQLite、这类库 - 任何不是 100% 受您控制的代码。我也没有断言它是用于 Java 或 .NET,我断言人们倾向于以这种方式使用异常,因为他们以前是 Java 或 .NET 开发人员。
    • 啊,我现在见到你了。非常感谢您的澄清!非常感谢。
    【解决方案2】:

    我不是 Apple 关于异常的建议的忠实拥护者,因为我认为异常范式通常会导致代码更简洁,因为您不必在其上乱扔额外的 NSError** 参数,也不必搁置用于指示错误的特殊返回值。

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

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

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

    【讨论】:

    • 长和短的解决方案是在流操作中从 libxml2 中捕获 -1 响应值,并在处理错误时进行缓慢且受控的退出堆栈。这而不是更简单的抛出异常的方法,这种方法显然不起作用,如果是的话,似乎也不可取。
    猜你喜欢
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多