views:

73

answers:

2

I want my app to continue gracefully when the online server messes up. I tried to wrap the dangerous line in a @try block. Yet it is still crashing like so:

the method:

+ (NSArray *)findAllFor:(NSObject *)ratable {
    NSString *ratingsPath = [NSString stringWithFormat:@"%@%@/%@/%@%@",
     [self getRemoteSite],
     [ratable getRemoteCollectionName],
     [ratable getRemoteId],
     [self getRemoteCollectionName],
     [self getRemoteProtocolExtension]];

    Response *res = [ORConnection get:ratingsPath withUser:[[self class] getRemoteUser] 
    andPassword:[[self class] getRemotePassword]];
 NSArray *ratings;
 @try {
  ratings = [self fromXMLData:res.body];
 }
 @catch (NSException *e) {
  ratings = [NSArray array];
 }
    return ratings;
}

the stack trace:

Program received signal: “SIGABRT”. 2010-08-07 16:38:51.846 TalkToHer[68608:7003] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]' *** Call stack at first throw:
( 0 CoreFoundation 0x02932919 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x02a805de objc_exception_throw + 47
2 CoreFoundation 0x0292858c -[__NSArrayI objectAtIndex:] + 236
3 TalkToHer 0x00009fa7 -[FromXMLElementDelegate parser:didEndElement:namespaceURI:qualifiedName:] + 425
4 Foundation 0x0017bcc1 _endElementNs + 453
5 libxml2.2.dylib 0x02d9deb6 xmlParseXMLDecl + 1353
6 libxml2.2.dylib 0x02da8bc1 xmlParseChunk + 3985
7 Foundation 0x0017b4c2 -[NSXMLParser parse] + 321
8 TalkToHer 0x0000b14d +[NSObject(XMLSerializableSupport) fromXMLData:] + 201
9 TalkToHer 0x00031a6c +[Rating findAllFor:] + 320
10 TalkToHer 0x00032d67 -[FirstClassContentPiece(Ratable) updateRatings] + 96
11 TalkToHer 0x00004d5f __-[InspirationController tableView:didSelectRowAtIndexPath:]_block_invoke_3 + 33
12 libSystem.B.dylib 0x9792efe4 _dispatch_call_block_and_release + 16
13 libSystem.B.dylib 0x97921a4c _dispatch_queue_drain + 249
14 libSystem.B.dylib 0x979214a8 _dispatch_queue_invoke + 50
15 libSystem.B.dylib 0x979212be _dispatch_worker_thread2 + 240
16 libSystem.B.dylib 0x97920d41 _pthread_wqthread + 390
17 libSystem.B.dylib 0x97920b86 start_wqthread + 30
) terminate called after throwing an instance of 'NSException'

is my syntax for @try @catch wrong? I attempted to add a @catch block for NSRangeException but it seems that's not the right approach (it's not a class).

also, the server error is caused by [ratable getRemoteId] sometimes returning (null) instead of an integer. this behavior seems pretty unpredictable; if anyone has a clue why ObjectiveResource might be doing that it would be helpful. but I still would like to know how to use @try @catch.

+1  A: 

You don't. You fix your code to not throw an exception under these circumstances.

Mike Abdullah
Okay, I understand that exceptions are sometimes regarded as a last resort in Cocoa, but I still want to know how to correctly use them. Especially since that code was written by somebody else - who knows what might break if I change it.
schwabsauce
No, exceptions are officially regarded as "programmer error." I appreciate the code wasn't written by you, but trying to handle the exception in some way will end in tears (memory management etc.). Instead fix that code.
Mike Abdullah
(If you can't tell, your error is in `-[FromXMLElementDelegate parser:didEndElement:namespaceURI:qualifiedName:]`, not the method you listed.)
kperryua
A: 

As I now understand it, throwing exceptions should only be done to alert users of your library that they have made a programming error. I am still curious why the syntax I used did not prevent the crash. I know the error was occurring several levels down; but the @try {} @catch {} block should handle all methods called by the methods I call...

At any rate, here is the fixed code, for anyone who wants to fetch scoped objects from a Rails-style restful resource.

+ (NSArray *)findAllFor:(NSObject *)ratable {
    NSString *ratingsPath = [NSString stringWithFormat:@"%@%@/%@/%@%@",
                          [self getRemoteSite],
                          [ratable getRemoteCollectionName],
                          [ratable getRemoteId],
                          [self getRemoteCollectionName],
                          [self getRemoteProtocolExtension]];

    Response *res = [ORConnection get:ratingsPath withUser:[[self class] getRemoteUser] 
                      andPassword:[[self class] getRemotePassword]];
    NSError **aError;
    if([res isError]) {
        *aError = res.error;
        return nil;
    }
    else {
        return [self performSelector:[self getRemoteParseDataMethod] withObject:res.body];
    }
}
schwabsauce