0

I am new to iPhone/Objective-C development, I am successfully parsing XML with NSXMLParser but I can't get exceptions to work properly. I'd like to use exceptions to deal with unexpected XML.

I'm wrapping the code for creating the NSXMLParser object and sending the setDelegate and parse messages to the object inside a @try @catch block, catching @NSException.

If I put NSAssert(FALSE, @"error) inside the @try block, the exception is caught properly. If, however, I have an NSAssert fail inside the delegate calls (eg, didStartElement, didEndElement, foundCharacters), then the program dies (in iPhone Simulator, haven't tried device yet). The debugger stack trace shows the assertion was raised into an exception but it doesn't pull back out into the top level code where the @try block is around the [parser parse] message call. Instead I get "Terminating app due to uncaught exception."

Please let me know if this is a known problem or if I'm doing something silly here.

Thanks -- Alex

Some code to make more concrete; no attempt to make this code correct for memory/releases/etc.

@implementation XMLTester

+(void)runXMLTester
{
    BOOL success = FALSE;
    XMLTester *tester = [[XMLTester alloc] init];
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query=KSFO"]];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
    [parser setDelegate:tester];
    @try {
        //NSAssert(FALSE, @"error"); // this assertion works fine
        success = [parser parse];
    }
    @catch (NSException * e) {
        success = FALSE;
        NSLog(@"Exception caught %@: %@", [e name], [e reason]);
    }
    @finally {
        NSLog(@"runXMLTester @finally block hit");
    }
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
    NSLog(@"Starting element %@", elementName);
    NSAssert(FALSE, @"error"); // this assertion does not work - does not hit @try block around parse message
}
Kampai
  • 22,848
  • 21
  • 95
  • 95
Alex
  • 3
  • 1
  • Passing exceptions through framework code is always a really bad idea. You should not be raising exceptions in your delegate methods. – Lily Ballard Nov 23 '10 at 06:01
  • Kevin - thanks, I'll be wary of that pattern; tc - yes, the real code is more careful but I need to learn proper memory management patterns. – Alex Nov 24 '10 at 02:20

2 Answers2

0

According to Bill Bumgarner, catching exceptions in the iPhone Simulator doesn't work correctly. Your best bet is to stop using exceptions here, as it's not really appropriate anyway. You should be calling -[NSXMLParser abortParsing] instead.

Community
  • 1
  • 1
Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • Thanks, very helpful. I like using assertions but believe in using only for things that would be programmer error - someone else changing their XML isn't really programmer error so best to treat as a real possible error. And shouldn't use exceptions here for the framework-specific reasons you've given. Thanks! – Alex Nov 24 '10 at 02:24
0

Don't use exceptions for flow control. Writing exception-safe (refcounted) Obj-C code is a bit of a pain — in particular, commonly-used stuff like Foo * foo = [[Foo alloc] init]; [foo doStuff]; [foo release]; foo = nil; will leak and [foo lock]; [foo doStuff]; [foo unlock]; will probably deadlock. You can mitigate the former by always autoreleasing immediately (I always do to prevent memory leaks when refactoring code), except you can't autorelease autorelease pools. The latter is hard to avoid unless you sprinkle @try/@finally everywhere.

Additionally, I strongly recommend breakpointing objc_exception_throw(). Sometimes Xcode seems to miss the throw and drop you into the debugger when abort() is called from uncaught_exception_handler() (or whatever it's called) after the stack's been unhelpfully unwound. And several things (notably CoreAnimation) catch, log, and otherwise ignore exceptions, which is a pain to debug unless you're watching the long.

There's one case in an app where I used an exception for control flow (I think I gave it the name "ControlThrow"); every time I hit that breakpoint I'm tempted to replace it with a goto.

tc.
  • 33,468
  • 5
  • 78
  • 96
  • I agree not using exceptions for flow control - wasn't my intent, Kevin's suggestion to use abortParsing helps make that easier. Thank you for the other suggestions here. – Alex Nov 24 '10 at 02:23