Возникло исключение Obj-C из функции обратного вызова обработчика ошибок libxml (Obj-C ++) в Obj-C - PullRequest
0 голосов
/ 23 ноября 2010

Я создал обработчик ошибок libxml в файле класса Obj-C MyStreamHandler.mm и назначил его анализатору потока с помощью xmlTextReaderSetErrorHandler. В обработчике ошибок я поднимаю исключение Obj-C, используя [NSException raise:format:]. Однако исключение не обнаруживается в моем классе Obj-C в его блоке @try/@catch.

Обратный вызов запускается, когда встречается недопустимый xml (такой зад </ foo </bar>) внутри libxml. Обработчик ошибок вызывается так, что многое наверняка, и, как известно, 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>

Ниже приведен связанный стек вызовов, вызванный исключением uncaught :

*** 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 ++ и обрабатывать его с помощью обычного блока @ try / @ в моем коде Obj-C? Или мне нужно вызвать фактическое исключение Obj-C ++ с собственным блоком try / catch Obj-C ++?

Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

1 голос
/ 23 ноября 2010

Исключения в Cocoa и Cocoa Touch зарезервированы только для ошибок программиста;вы не должны ожидать их использования в производственном коде, поставляемом конечным пользователям.

Вы также не можете ожидать, что вы поймете исключение, брошенное через границу API, только в ситуациях, когда вы контролируете весь промежуточный стеккадры.Таким образом, в приведенном вами примере вы выкидываете исключение из кода, который ничего не знает об исключениях Objective-C и, следовательно, не может остаться в допустимом состоянии.

У вас будетчтобы ваш обработчик ошибок передавал информацию об ошибке в код более высокого уровня другим способом.Именно поэтому API-интерфейсы обратного вызова и регистрации обратного вызова имеют аргумент void * (например, последний аргумент xmlTextReaderSetErrorHandler, который становится первым аргументом функции-обработчика) - он позволяет вам связать некоторый контекст с вашим обратным вызовом, чтобы вы моглисделайте что-то вроде определения, какой из нескольких возможных документов, которые вы, возможно, читаете, вызвал ошибку.

0 голосов
/ 24 ноября 2010

Я не большой поклонник совета Apple по исключениям, так как я думаю, что парадигма исключений обычно приводит к более чистому коду, так как вам не нужно засорять его дополнительными NSError** параметрами, и вам не нужно откладывать в сторону специальные возвращаемые значения для обозначения ошибки.

Однако вы абсолютно не можете позволить исключениям Objective-C распространяться по коду, который их не ожидает. Это потому, что довольно много кода не написано для обработки исключений Objective-C. Например, libxml2 почти наверняка не размотает свой стек должным образом, что приведет к утечкам и другим неприятностям.

Я понятия не имею, почему ваше обработчик не перехватывается вашим обработчиком (может быть, вам нужно @throw его), но это не имеет значения. Вы не можете использовать их в этой ситуации.

То, что вы должны сделать при обратном звонке, это просто заметить, что у вас произошла ошибка, возможно, зарегистрируйте ее или запишите, а затем верните в обычном режиме. Если ошибка является фатальной, libxml2 будет знать, как прекратить работу, без вашей помощи.

...