Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usage of NSException in iPhone Apps

One of my friends asked me not to use NSException in iPhone Apps. The reason he gave was "performance bottleneck". But I am not convinced of it.

Can someone confirm me that we should restrain from using NSException in iPhone App? If you have best practices for using NSException, please provide that too.

UPDATE:

This link requests us to use Exception handling at the app level. Have someone ever done it? Please give the advantages of it and any other performance hitches it could create.

like image 679
RK- Avatar asked Nov 30 '10 04:11

RK-


People also ask

What is exception in iOS?

In short, exceptions cause applications to crash if left unhandled. They generally occur when trying to perform an operation on an object incorrectly, such as using an out-of-bounds index to access an array item, or passing nil to a method that doesn't accept it.

What is an NSE exception?

In general, the National Security Exception (NSE) is invoked to remove procurements from the obligations of Canada's trade agreements for reasons of national security. The procurement itself must either be indispensable for national security or indispensable for national defence purposes.

Which of the following is used when creating a NSError object?

The core attributes of an NSError object are an error domain (represented by a string), a domain-specific error code and a user info dictionary containing application specific information.


2 Answers

In short:

Do not use exceptions to indicate anything but unrecoverable errors

It is only appropriate to use @try/@catch to deal with unrecoverable errors. It is never appropriate to use @throw/@try/@catch to do control-flow like operations on iOS or Mac OS X. Even then, consider carefully whether you are better off using an exception to indicate an unrecoverable error or simply crashing (call abort()); a crash often leaves behind significantly more evidence.

For example, it would not be appropriate to use for catching out-of-bounds exceptions unless your goal is to catch them and somehow report the error, then -- typically -- crash or, at the least, warn the user that your app is in an inconsistent state and may lose data.

Behavior of any exception thrown through system framework code is undefined.


Can you explain the "Behavior of any exception thrown through system framework code is undefined." in detail?

Sure.

The system frameworks use a design where any exception is considered to be a fatal, non-recoverable, error; a programmer error, for all intents and purposes. There are a very limited number of exceptions (heh) to this rule.

Thus, in their implementation, the system frameworks will not ensure that everything is necessarily properly cleaned up if an exception is tossed that passes through system framework code. Sine the exception is, by definition, unrecoverable, why pay the cost of cleanup?

Consider this call stack:

your-code-1()
    system-code()
        your-code-2()

I.e. code where your code calls into system code which calls into more of your code (a very common pattern, though the call stacks are obviously significantly deeper).

If your-code-2 throws an exception, that the exceptions passes over system-code means the behavior is undefined; system-code may or may not leave your application in an undefined, potentially crashy or data-lossy, state.

Or, more strongly: You can't throw an exception in your-code-2 with the expectation that you can catch and handle it in your-code-1.

like image 188
bbum Avatar answered Sep 19 '22 04:09

bbum


I've used exception handling for my fairly intensive audio app with no problems whatsoever. After much reading and a bit of benchmarking and disassembly analysis, I've came to the controversial conclusion that there is no real reason not to use them (intelligently) and plenty of reason not to (NSError pointer-pointers, endless conditionals...yuck!). Most of what people are saying on the forums is just repeating the Apple Docs.

I go into quite a bit of detail in this blog post but I'll outline my findings here:

Myth 1: @try/@catch/@finally is too expensive (in terms of CPU)

On my iPhone 4, throwing and catching 1 million exceptions takes about 8.5 seconds. This equates to about only 8.5 microseconds for each. Expensive in your realtime CoreAudio thread? Maybe a bit (but you'd never throw exceptions there would you??), but an 8.5μs delay in the UIAlert telling the user there was a problem opening their file, is it going to be noticed?

Myth 2: @try Blocks Have a Cost On 32bit iOS

The Apple docs speak of "zero-cost @try blocks on 64bit" and state that 32 bit incurs a cost. A little benchmarking and disassembly analysis seems to indicate that there is zero-cost @try blocks on 32bit iOS (ARM processor) as well. Did Apple mean to say 32bit Intel?

Myth 3: It Matters that Exceptions Thrown Through Cocoa Frameworks are Undefined

Yes, they are "undefined", but what are you doing throwing through the Apple framework anyway? Of course Apple doesn't handle them for you. The whole point of implementing exception handling for recoverable errors is to handle them locally - just not every-single-line "locally".

One edge case here is with methods like NSObject:performSelectorOnMainThread:waitUntilDone:. If the later parameter is YES, this acts like a synchronous function in which case you might be forgiven for expecting the exception to bubble up to your calling scope. For example:

/////////////////////////////////////////////////////////////////////////
#pragma mark - l5CCThread
/////////////////////////////////////////////////////////////////////////

@interface l5CCThread : NSThread @end

@implementation l5CCThread

- (void)main
{
    @try {
        
        [self performSelectorOnMainThread:@selector(_throwsAnException) withObject:nil waitUntilDone:YES];
        
    } @catch (NSException *e) {
        NSLog(@"Exception caught!");
    }
}
- (void)_throwsAnException { @throw [NSException exceptionWithName:@"Exception" reason:@"" userInfo:nil]; }

@end

/////////////////////////////////////////////////////////////////////////
#pragma mark - l5CCAppDelegate
/////////////////////////////////////////////////////////////////////////

@implementation l5CCAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    l5CCThread *thd = [[l5CCThread alloc] init];
    [thd start];
    
    return YES;
}
// ...

In this case the exception would pass "through the cocoa framework" (the main thread's run loop) missing your catch and crashing. You can easily work around this using GCD's dispatch_synch and putting in it's block argument the method call plus any exception handling.

Why Use NSException's over NSError's

Anyone who's ever done work in one of the older C-based frameworks like Core Audio knows what a chore it is checking, handling and reporting errors. The main benefit @try/@catch and NSExceptions provides is to make your code cleaner and easier to maintain.

Say you have 5 lines of code which work on a file. Each might throw one of, say, 3 different errors (eg out of disk space, read error, etc.). Instead of wrapping each line in a conditional which checks for a NO return value and then outsources the NSError pointer investigation to another ObjC method (or worse, uses a #define macro!), you wrap all 5 lines in a single @try and handle each error right there. Think of the lines you'll save!

By creating NSException subclasses, you can also easily centralise error messages, and avoid having your code littered with them. You can also easily distinguish your app's "non-fatal" exceptions from fatal programmer errors (like NSAssert). You can also avoid the need for the "name" constant (the subclass's name, is the "name").

Examples of all this and more details about the benchmarks and disassembly are on this blog post...

Exceptions plus try/catch/finally is the paradigm used by pretty much every other major language (C++, Java, PHP, Ruby, Python). Maybe its time to drop the paranoia and embrace it as well...at least in iOS.

like image 21
Hari Honor Avatar answered Sep 22 '22 04:09

Hari Honor