views:

483

answers:

3

Coming from a Symbian background, I'm somewhat concerned about the seeming lack of error handling in Cocoa. Cocoa has many methods that, as far as I can see, have no error reporting and yet could fail.

Eg How come NSMutableString appendString has a void return type and doesn't throw exceptions (at least the documentation doesn't mention any)? Surely if I append a long enough string then theoretically I could run out of memory. Is it paranoid of me to check the length of the NSMutableString before and after appending to verify that the append worked?

A: 

This may be of some help: Cocoa Error Handling

ennuikiller
Thanks for the link, but the I don't get any NSError from NSMutableString appendString, or am I missing something?The "best" advice I have seen in the document is "A Note on Errors and Exceptions" in which it says: Although exceptions should ideally be taken care of before deployment, a shipped application can still experience exceptions as a result of some truly exceptional situation such as “out of memory” or “boot volume not available.” It is best to allow the highest level of the application—NSApp itself—to deal with these situations.
David Jacobson
+5  A: 

My test is on Mac OS X and I imagine you're talking about the iPhone platform.

The thing is I fail to see how returning an error from the appendString method would help since the platform is at that point in such a state that it can't satisfy malloc requests for your process.

To get around the problem you can probably malloc your own address space and use this process managed memory as storage for your strings. I think Carbon's CFString (toll free bridged to NSString) allows you to use your own memory allocator.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) 
{
    NSAutoreleasePool * pool = [NSAutoreleasePool new];
    NSMutableString * m = [NSMutableString stringWithCapacity:100000];

    int i;
    for(i=0;i<1000000;i++)
     [m appendString:@"ABCDE..."]; //1400 characters long
    [pool release];
}



cd:tmp diciu$ ./a.out 
a.out(2216) malloc: *** mmap(size=1220067328) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
[..]
2009-08-13 16:45:44.163 a.out[2216:10b] *** Terminating app due to uncaught exception     'NSMallocException', reason: 'Out of memory. We suggest restarting the application. If you     have an unsaved document, create a backup copy in Finder, then try to save.'
2009-08-13 16:45:44.165 a.out[2216:10b] Stack: (
    2494541803,
    2485014075,
    2435399864,
    2494157025,
    2494172776,
    2434276628
)
Trace/BPT trap
diciu
Thank you, diciu, for putting me on the right track.Apparently, an exception *is* thrown.I'll write up the answer.
David Jacobson
Unfortunately, things are not so simple for iPhone.I used @trap and @catch around code similar to yours, and in the emulator I caught an (obsolete!) NSMallocException.Unfortunately, when debugging on the device I get:<pre>Program received signal: “0”.warning: check_safe_call: could not restore current frame</pre>
David Jacobson
If your application is really likely to run into memory issues you should prolly manage memory on your own. You can then call stuff that writes in your memory storage only if there's room for it.In real life I don't think you ever need to worry about appendString failing because of memory exhaustion.
diciu
Thanks for all your help, diciu.
David Jacobson
+2  A: 

I modified diciu's code testing NSMutableString appendString to fail quicker and to try to catch any exceptions. Here is the core section:

    NSMutableString * m = [[NSMutableString alloc] initWithCapacity:1000];
    NSMutableString * n = [[NSMutableString alloc] initWithCapacity:1000];
    @try {
        [m appendString:@"1234567890"];
        [n appendString:m];
        int i;
        for(i=0;i<100;i++) {
            [m appendString:n];
            [n appendString:m];
        }
    }
    @catch (id exception) {
        NSLog(@"!!Exception");
    }
//    @catch (NSException *exception) {
//        NSLog(@"!!Exception: %@", [exception name]);
//    }
    @finally {
        [m release];
        [n release];
        NSLog(@"Finally...");
    }

I tested on various platforms:

Mac OS X 10.5.8 & Xcode 3.1.3 iPhone 3.0 Emulator: An exception thrown (NSException, with name NSMallocException). This is good news but strange since NSMallocException is supposedly obsolete.

iPhone 3.0.1: the app crashes.

The bad news is that for the iPhone it seems like there is no way to catch this error.

The good news is that there is no point defensively programming, since the whole application blows up anyway!

I hope someone has a better answer than this.

David Jacobson
For what it's worth on 10.5.8 i386 4Gb RAM the code above does not hang, it throws an exception (out of memory).
diciu
Thanks diciu; you are more patient than me. When I tested I didn't wait around long enough to catch the exception. I'll fix my text above.
David Jacobson