1

I understand what the @try and @catch blocks do, but what does @throw do?

@try{

/**Insert code here**/

}

@catch(NSException * myException){

/**insert code **/

}

Where does @throw fit in all this? A simple example would be really helpful.

I checked out this link but couldn't find the answer I was looking for.

https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Exceptions/Tasks/RaisingExceptions.html

Brosef
  • 2,945
  • 6
  • 34
  • 69
  • 4
    throw something so you can catch it – Bryan Chen Jul 26 '13 at 03:59
  • so in order to catch it, would throw always be within the @try block? Can you only throw exceptions? – Brosef Jul 26 '13 at 04:04
  • You can only throw exceptions, though you can define your own classes which extend `NSException` if you need more information. The try block defines a block of code where you are anticipating exceptions to be thrown. If any code within the try block throws an exception, it will be caught in the corresponding catch block. If an exception is thrown by some code that wasn't inside of a try block, (or there was no catch to catch the exception), then you have an "unhandled exception". – wardd Jul 26 '13 at 04:29
  • 2
    To be blunt: **Exceptions should only be used for non-recoverable errors.** you should not try to recover and continue in an @catch. – bbum Jul 26 '13 at 04:37
  • OP - If you have found a resolve to your question, be sure to mark the appropriate answer as correct. – user Jul 28 '13 at 21:30

3 Answers3

2

Exceptions in Objective-C are used mainly for cases of obvious programmer error, such as attempting to access an element outside the bounds of an NSArray. In contrast, some programming languages use exceptions to specify any sort of recoverable error such as "file not found", but in Objective-C and Cocoa, an NSException that has been thrown with @throw typically means there is nothing that can be done to recover.

In either case, the analogy is that exceptions are "thrown" to the catcher, no matter how many function calls sit between the thrower and the catcher. This is in contrast to functions that return error statuses through return values or output parameters. Functions that return error statuses need to ensure that all other functions in the chain return the same error status in order for the original caller at the other end to know there was an error. Exceptions that are thrown bypass all intermediate functions and give the error straight to where it is wanted, the original call-site.

void function3()
{
    @throw [NSException exceptionWithName:@"Test" reason:@"Test" userInfo:nil];
}

void function2()
{
    function3();
}

void function1()
{
    function2();
}

int main()
{
    @try
    {
        function1();
    }
    @catch(NSException *ex)
    {
        // handle exception that occurred 3 functions deep
    }
}

In general, you should avoid throwing exceptions in Objective-C as a general means of catching recoverable errors because of the way memory management works. In the above, if any objects were allocated in function1, function2 or function3 but were not autoreleased, then the memory for these objects would be leaked. ARC with "full exception safety" enabled is capable of detecting these cases and handling them appropriately but this only applies for objects in your own code, not any memory allocated by external libraries that you could be using. There is the general expectation that your application will not recover from an exception thrown with @throw.

See more details in this Q&A.

Community
  • 1
  • 1
dreamlax
  • 93,976
  • 29
  • 161
  • 209
1

@throw lets you, wait for it, throw an exception, and why we want to throw an exception? We throw exceptions when exceptional things happen, for example, in the following code, it doesn't make sense that the inventory array size is zero, so we throw an exception, for that special case.

NSString *getRandomCarFromInventory(NSArray *inventory) {
    int maximum = (int)[inventory count];
    if (maximum == 0) {
        NSException *e = [NSException
                         exceptionWithName:@"EmptyInventoryException"
                         reason:@"*** The inventory has no cars!"
                         userInfo:nil];
        @throw e;
    }
    int randomIndex = arc4random_uniform(maximum);
    return inventory[randomIndex];
}

This example an further information on: http://rypress.com/tutorials/objective-c/exceptions.html (emphasis mine)

Two distinct types of problems can arise while an iOS or OS X application is running. Exceptions represent programmer-level bugs like trying to access an array element that doesn’t exist. They are designed to inform the developer that an unexpected condition occurred. Since they usually result in the program crashing, exceptions should rarely occur in your production code.

jsedano
  • 4,088
  • 2
  • 20
  • 31
  • 3
    That's typically the case where you don't want to throw an exception. An exception in Objective-C is supposed to notify that the app is in a unrecoverable state, meaning that the app can't function properly after the exception is thrown. – ldiqual Jul 26 '13 at 04:23
  • 2
    It depends whether getting a random car from an empty inventory is recoverable or not. If there absolutely must be cars in the inventory then this is probably a case for an `NSException`, on the other hand, if this function could return `nil` and the original caller is able to do an alternative action if there are no cars, then an `NSException` should not be used. – dreamlax Jul 26 '13 at 04:33
1

If an operation fails and raises an exception, like in:

@try{ // code fails and raises an exception 
      if (!objectNotInMemory)
      {
          @throw exc; // presuming you already initialized NSException* exc
      }
}
@catch (NSException* e){ // deal with exception exc
    if ([[e reason] isEqualToString:@"Object not in memory"])
    UIAlertView* alert = [UIAlertView initWith...];
}

the thing raising that exception (in this case, objectNotInMemory being nonexistent, nil, or 0) has an @throw{} directive that creates or passes an NSException object or message of some sort. This exception object can then be caught in an @catch{} block or if ignored, will alert the operating system to shutdown the application process.

An exception must be thrown to be caught.

More reading from Apple's docs (which you should get comfortable with):

@try —Defines a block of code that is an exception handling domain: code that can potentially throw an exception.

@catch() —Defines a block containing code for handling the exception thrown in the @try block. The parameter of @catch is the exception object thrown locally; this is usually an NSException object, but can be other types of objects, such as NSString objects.

@finally — Defines a block of related code that is subsequently executed whether an exception is thrown or not.

@throw — Throws an exception; this directive is almost identical in behavior to the raise method of NSException. You usually throw NSException objects, but are not limited to them. For more information about @throw, see “Throwing Exceptions.”

user
  • 3,388
  • 7
  • 33
  • 67
  • Thanks for the detailed answered. So you're saying if objectNotInMemory is not true, then throw exc, which is an exception. Then the catch block of code catches the exception and sets e equal to exc? Can we consider e and exc pointers that reference exception objects? – Brosef Jul 26 '13 at 06:14
  • to initialize exc, would you just do NSException * exc = [[NSException alloc] init]; – Brosef Jul 26 '13 at 07:28
  • @somid- yes, you would initialize it like any object. You could `[[...alloc] init]` and then set properties one by one, but I prefer an `[[...alloc] initWithName:(NSString*) reason:(NSString*) userInfo:(NSDictionary*)]` – user Jul 26 '13 at 17:54
  • Also note that it is far better practice to write robust code in the first place as opposed to raising exceptions to make up for potential errors. – user Jul 26 '13 at 17:58
  • @user: Your comment regarding parameters being passed by value and not by reference is false. `@catch` does receive the same object that was thrown, because **(a)** the exception is immutable and copying it would be pointless, and **(b)** because although parameters are passed by value in C, pointers are values and Objective-C objects are [almost] always handled by pointers, and **(c)** because it can be verified with a minimal example. Create an `NSException` object, and before throwing it with `@throw`, log its address. Then in the `@catch` block, log its address again. It will be the same. – dreamlax Jul 31 '13 at 07:13
  • @dreamlax thanks for the update- was not aware that exceptions are passed by address. – user Jul 31 '13 at 16:22