425

What's the best way to throw an exception in objective-c/cocoa?

Pramod More
  • 1,220
  • 2
  • 22
  • 51
Steph Thirion
  • 9,313
  • 9
  • 50
  • 58
  • **Don't throw `NSException`, Swift can not catch it (at least not without workaround), easily [see how to create NSError](https://stackoverflow.com/a/4654759/8740349) (and use `@throw`)** – Top-Master Sep 12 '21 at 00:29

13 Answers13

536

I use [NSException raise:format:] as follows:

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
cwharris
  • 17,835
  • 4
  • 44
  • 64
e.James
  • 116,942
  • 41
  • 177
  • 214
  • 9
    I prefer this way as apposed to the `@throw([NSException exceptionWith…])` approach as it more concise. – Sam Soffes Jun 18 '10 at 19:56
  • 9
    Be sure to read the important caveat from harms (http://stackoverflow.com/questions/324284/324805#324805) – e.James Sep 22 '10 at 02:11
  • 28
    I generally prefer this too, but there is one gotcha. Might just be my current version of Xcode, but the [NSException raise...] syntax doesn't seem to be recognized by the parser as an exit path from a method which returns a value. I am seeing the warning "Control may reach end of non-void function" when using this syntax, but with the @throw([NSException exceptionWith…]) syntax, the parser recognizes that as an exit and does not display the warning. – mattorb Aug 27 '11 at 16:32
  • 1
    @mpstx I always use the throw syntax for the reason you've given (which is still relevant two years later in Xcode 4.6, and probably always will be). Having the IDE recognise that throwing an exception is a function exit point frequently matters if you want to avoid warnings. – Mark Amery Jul 29 '13 at 11:13
  • FWIW I'm noticing that @try/@catch blocks also result in a false-negative for "control reaches end of non-void function" warnings (i.e. the warning isn't displayed when it should be) – Brian Gerstle Mar 24 '15 at 02:31
  • *Warning:* As @harms mention in their answer, Apple's frameworks (and most other third-party Objective-C frameworks) are not exception-safe. You _will_ leak memory throwing out of methods called by AppKit/UIKit and through Apple code. – uliwitness Jul 06 '18 at 18:39
258

A word of caution here. In Objective-C, unlike many similar languages, you generally should try to avoid using exceptions for common error situations that may occur in normal operation.

Apple's documentation for Obj-C 2.0 states the following: "Important: Exceptions are resource-intensive in Objective-C. You should not use exceptions for general flow-control, or simply to signify errors (such as a file not being accessible)"

Apple's conceptual Exception handling documentation explains the same, but with more words: "Important: You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime. [.....] Instead of exceptions, error objects (NSError) and the Cocoa error-delivery mechanism are the recommended way to communicate expected errors in Cocoa applications."

The reasons for this is partly to adhere to programming idioms in Objective-C (using return values in simple cases and by-reference parameters (often the NSError class) in more complex cases), partly that throwing and catching exceptions is much more expensive and finally (and perpaps most importantly) that Objective-C exceptions are a thin wrapper around C's setjmp() and longjmp() functions, essentially messing up your careful memory handling, see this explanation.

Ben Lings
  • 28,823
  • 13
  • 72
  • 81
harms
  • 8,728
  • 4
  • 30
  • 27
  • 11
    I think this applies to most programming languages: "try to avoid using exceptions for common error situations". The same applies in Java; it's bad practice to handle user input errors (for example) with exceptions. Not just because of resource usage, but also for code clarity. – beetstra Dec 07 '10 at 12:58
  • 6
    More importantly, Exceptions in Cocoa are designed to indicate non-recoverable program errors. Doing otherwise is running against the framework and can lead to undefined behaviour. See http://stackoverflow.com/questions/3378696/iphone-try-end-try/3379240#3379240 for details. – KPM Jan 23 '12 at 01:45
  • 9
    "The same applies in Java;" Disagree. You can use checked exceptions in Java just fine for normal error conditions. Of course you wouldn't use Runtime exceptions. – Daniel Ryan Apr 01 '13 at 22:01
  • I prefer the Cocoa way (exceptions are for programmer errors only) so I'd _prefer_ to do it in Java as well, but the reality is that you should go with typical practices in an environment, and exceptions for error handling sticks out like a bad smell in Objective-C, but are used a lot for that purpose in Java. – gnasher729 Mar 24 '14 at 12:46
  • 1
    This comment does not reply the question. Maybe the OP just wants to crash the app to test whether the crash report framework is working as expected. – Simon Mar 19 '15 at 05:21
  • new link to apple documentation : https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html – machei Oct 13 '15 at 15:11
62
@throw([NSException exceptionWith…])

Xcode recognizes @throw statements as function exit points, like return statements. Using the @throw syntax avoids erroneous "Control may reach end of non-void function" warnings that you may get from [NSException raise:…].

Also, @throw can be used to throw objects that are not of class NSException.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • 11
    @Steph Thirion: See http://developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/Tasks/RaisingExceptions.html for all of the details. Bottom line? Both will work, but @throw can be used to throw objects that are not of class NSException. – e.James Nov 27 '08 at 21:18
33

Regarding [NSException raise:format:]. For those coming from a Java background, you will recall that Java distinguishes between Exception and RuntimeException. Exception is a checked exception, and RuntimeException is unchecked. In particular, Java suggests using checked exceptions for "normal error conditions" and unchecked exceptions for "runtime errors caused by a programmer error." It seems that Objective-C exceptions should be used in the same places you would use an unchecked exception, and error code return values or NSError values are preferred in places where you would use a checked exception.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Daniel Yankowsky
  • 6,956
  • 1
  • 35
  • 39
  • 1
    Yes this is correct (after 4 years though :D ), create your own error class ABCError which extends from the NSError class and use it for checked exceptions rather than NSExceptions. Raise NSExceptions where the programmer errors (unexpected situation such as a number format issue) occurs. – chathuram May 15 '13 at 16:45
15

I think to be consistant it's nicer to use @throw with your own class that extends NSException. Then you use the same notations for try catch finally:

@try {
.....
}
@catch{
...
}
@finally{
...
}

Apple explains here how to throw and handle exceptions:

  1. Catching Exceptions
  2. Throwing Exceptions
rptwsthi
  • 10,094
  • 10
  • 68
  • 109
rustyshelf
  • 44,963
  • 37
  • 98
  • 104
14

Since ObjC 2.0, Objective-C exceptions are no longer a wrapper for C's setjmp() longjmp(), and are compatible with C++ exception, the @try is "free of charge", but throwing and catching exceptions is way more expensive.

Anyway, assertions (using NSAssert and NSCAssert macro family) throw NSException, and that sane to use them as Ries states.

Psycho
  • 1,863
  • 1
  • 16
  • 17
  • Good to know! We have a third party library which we don't want to modify that throws exceptions for even the smallest errors. We have to catch them in one place in the app and it just makes us cringe, but this makes me feel a bit better. – Yuri Brigance Jul 31 '14 at 20:35
8

Use NSError to communicate failures rather than exceptions.

Quick points about NSError:

  • NSError allows for C style error codes (integers) to clearly identify the root cause and hopefully allow the error handler to overcome the error. You can wrap error codes from C libraries like SQLite in NSError instances very easily.

  • NSError also has the benefit of being an object and offers a way to describe the error in more detail with its userInfo dictionary member.

  • But best of all, NSError CANNOT be thrown so it encourages a more proactive approach to error handling, in contrast to other languages which simply throw the hot potato further and further up the call stack at which point it can only be reported to the user and not handled in any meaningful way (not if you believe in following OOP's biggest tenet of information hiding that is).

Reference Link: Reference

NANNAV
  • 4,875
  • 4
  • 32
  • 50
Jason Fuerstenberg
  • 1,341
  • 13
  • 13
  • This comment does not reply the question. Maybe the OP just wants to crash the app to test whether the crash report framework is working as expected. – Simon Mar 19 '15 at 05:23
8

This is how I learned it from "The Big Nerd Ranch Guide (4th edition)":

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];
6

You can use two methods for raising exception in the try catch block

@throw[NSException exceptionWithName];

or the second method

NSException e;
[e raise];
Subbu
  • 2,063
  • 4
  • 29
  • 42
3

I believe you should never use Exceptions to control normal program flow. But exceptions should be thrown whenever some value doesn't match a desired value.

For example if some function accepts a value, and that value is never allowed to be nil, then it's fine to trow an exception rather then trying to do something 'smart'...

Ries

edwardmp
  • 6,339
  • 5
  • 50
  • 77
R. van Twisk
  • 434
  • 6
  • 14
0

You should only throw exceptions if you find yourself in a situation that indicates a programming error, and want to stop the application from running. Therefore, the best way to throw exceptions is using the NSAssert and NSParameterAssert macros, and making sure that NS_BLOCK_ASSERTIONS is not defined.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
0

Sample code for case: @throw([NSException exceptionWithName:...

- (void)parseError:(NSError *)error
       completionBlock:(void (^)(NSString *error))completionBlock {


    NSString *resultString = [NSString new];

    @try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    resultString = dictFromData[@"someKey"];
    ...


} @catch (NSException *exception) {

      NSLog( @"Caught Exception Name: %@", exception.name);
      NSLog( @"Caught Exception Reason: %@", exception.reason );

    resultString = exception.reason;

} @finally {

    completionBlock(resultString);
}

}

Using:

[self parseError:error completionBlock:^(NSString *error) {
            NSLog(@"%@", error);
        }];

Another more advanced use-case:

- (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock {

NSString *resultString = [NSString new];

NSException* customNilException = [NSException exceptionWithName:@"NilException"
                                                          reason:@"object is nil"
                                                        userInfo:nil];

NSException* customNotNumberException = [NSException exceptionWithName:@"NotNumberException"
                                                                reason:@"object is not a NSNumber"
                                                              userInfo:nil];

@try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    NSArray * array = dictFromData[@"someArrayKey"];

    for (NSInteger i=0; i < array.count; i++) {

        id resultString = array[i];

        if (![resultString isKindOfClass:NSNumber.class]) {

            [customNotNumberException raise]; // <====== HERE is just the same as: @throw customNotNumberException;

            break;

        } else if (!resultString){

            @throw customNilException;        // <======

            break;
        }

    }

} @catch (SomeCustomException * sce) {
    // most specific type
    // handle exception ce
    //...
} @catch (CustomException * ce) {
    // most specific type
    // handle exception ce
    //...
} @catch (NSException *exception) {
    // less specific type

    // do whatever recovery is necessary at his level
    //...
    // rethrow the exception so it's handled at a higher level

    @throw (SomeCustomException * customException);

} @finally {
    // perform tasks necessary whether exception occurred or not

}

}

Aleksandr B.
  • 505
  • 4
  • 6
-6

There is no reason not to use exceptions normally in objective C even to signify business rule exceptions. Apple can say use NSError who cares. Obj C has been around a long time and at one time ALL C++ documentation said the same thing. The reason it doesnt matter how expensive throwing and catching an exception is, is the lifetime of an exception is exceedingly short and...its an EXCEPTION to the normal flow. I have never heard anyone say ever in my life, man that exception took a long time to be thrown and caught.

Also, there are people that think that objective C itself is too expensive and code in C or C++ instead. So saying always use NSError is ill-informed and paranoid.

But the question of this thread hasnt yet been answered whats the BEST way to throw an exception. The ways to return NSError are obvious.

So is it: [NSException raise:... @throw [[NSException alloc] initWithName.... or @throw [[MyCustomException... ?

I use the checked/unchecked rule here slightly differently than above.

The real difference between the (using the java metaphor here) checked/unchecked is important --> whether you can recover from the exception. And by recover I mean not just NOT crash.

So I use custom exception classes with @throw for recoverable exceptions, because its likely I will have some app method looking for certain types of failures in multiple @catch blocks. For example if my app is an ATM machine, I would have a @catch block for the "WithdrawalRequestExceedsBalanceException".

I use NSException:raise for runtime exceptions since I have no way to recover from the exception, except to catch it at a higher level and log it. And theres no point in creating a custom class for that.

Anyway thats what I do, but if there's a better, similarly expressive way I would like to know as well. In my own code, since I stopped coding C a hella long time ago I never return an NSError even if I am passed one by an API.

deleted_user
  • 3,817
  • 1
  • 18
  • 27
  • 4
    I would recommend attempting to program a server with exceptions as part of the normal flow of error cases before making such generalizing statements as "there is no reason not to use exceptions normally in objective C". Believe it or not, there are reasons to write high performance applications (or at least parts of applications) in ObjC, and throwing exceptions normally seriously hampers performance. – jbenet Jul 16 '11 at 01:15
  • 7
    There are indeed very good reasons why not to use exceptions in Cocoa. See Bill Bumgarner's answer here for more: http://stackoverflow.com/questions/3378696/iphone-try-end-try/3379240#3379240 . He knows what he's talking about (hint: check his employer). Exceptions in Cocoa are treated as unrecoverable errors, and can leave the system in an unstable state. NSError is the way to go for passing general errors around. – Brad Larson Jul 26 '11 at 20:49
  • Exceptions are **exceptional**. Business rule failures surely do not qualify. "Finding and designing away exception-heavy code can result in a decent perf win." MSDN via http://www.codinghorror.com/blog/2004/10/creating-more-exceptional-exceptions.html – Jonathan Watmough Aug 12 '11 at 23:25
  • 3
    Exceptions can not be thrown from blocks. Exceptions thrown in an ARC environment could make your program leak. Thus the downvote. – Moszi Feb 24 '12 at 21:47
  • "it doesnt matter how expensive throwing and catching an exception is" I am writing an emulator where performance is critical. I can't throw a whole bunch of expensive exceptions. – NobodyNada Feb 08 '15 at 23:17
  • There are obviously exceptions. However, worrying about performance is over optimizing especially for applications. – Isaac Paul Apr 13 '23 at 20:05