Reputation: 61
I've created a libxml error handler in an Obj-C MyStreamHandler.mm class file and assigned it to the stream parser using xmlTextReaderSetErrorHandler
. In the error handler I am raising an Obj-C exception using [NSException raise:format:]
. However the exception is not being caught within my Obj-C class in it's @try/@catch
block.
The callback is triggered when invalid xml is encountered (such ass </foo</bar>) is encountered within libxml. The error handler is being called that much is certain, and the NSException
is also known to be raised. However the exception is not being caught in my Obj-C class that is the client of 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]; }
Here's a sample XML file that would trigger this callback and exception:
<foo><bar</foo>
Here's the associated call stack below caused by the uncaught exception:
*** 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'
Is this totally bogus? Am I able to throw a Obj-C exception from within an Obj-C++ function and handle that via a normal @try/@catch block in my Obj-C code? Or do I need to raise an actual Obj-C++ exception with it's own Obj-C++ try/catch block?
Any help would be much appreciated.
Upvotes: 0
Views: 808
Reputation: 86651
I'm not a great fan of Apple's advice on exceptions since I think the exception paradigm leads to cleaner code generally since you don't have to litter it with extra NSError**
parameters and you don't have to set aside special return values to indicate an error.
However you absolutely cannot allow Objective-C exceptions to propagate across code that is not expecting them. This is because quite a lot of code is not written to deal with Objective-C exceptions. libxml2
for instance will almost certainly not unwind its stack properly leading to leaks and other nastiness.
I have no idea why your exception is not caught by your handler (maybe you need to @throw
it) but that is irrelevant. You can't use them in this situation.
What you should do in your call back is merely note that you have had an error, maybe log or record it and then return normally. If the error is fatal, libxml2 will know how to terminate without you helping it.
Upvotes: 0
Reputation: 55096
Exceptions in Cocoa and Cocoa Touch are reserved for programmer errors only; you should not expect to use them in production code as shipped to end users.
You also can't generally expect to catch an exception thrown across an API boundary, only in situations where you control all of the intermediate stack frames. Thus in the example you give, you're throwing an exception out of code that doesn't have any knowledge of Objective-C exceptions, and thus may not be left in a valid state.
You'll have to have your error handler pass the error information out to your higher-level code in some other fashion. That's typically why callback and callback-registration APIs have a void *
argument (such as the final argument of xmlTextReaderSetErrorHandler
, which becomes the first argument of the handler function) - it allows you to associate some context with your callback so you can do something like determine which of multiple possible documents you may be reading have triggered the error.
Upvotes: 1