Exception handling techniques in OBJECTIVE-C applications are efficient solutions to cope with unusual circumstances. They automate the propagation of the exception from the point of detection to the point of management by decoupling the detection and handling of certain conditions. As a result, your code will be considerably cleaner, more straightforward to develop, and easier to maintain.
- Introduction
- How to implement
- Dealing with Exceptions
- Flow of special case taking care of utilizing compiler orders
- Exception Handling and Memory Management
- Listing 4 Releasing an autorelease pool containing an exception object
- Conclusion
Introduction:
The API reference for the Exception Handling framework may be found in this collection of documents. This framework includes tools for tracking and debugging unusual behavior in Objective-C programs.This collection currently contains only one class reference: the NSExceptionHandler class, which is described in NSExceptionHandler.h.
For crashes, the initial step is to utilize blunder messages and the debugger to sort out the thing call is causing the issue. On the off chance that the issue is brought about by an uncaught special case, read this Apple article on exemption dealing with. The particular response truly relies upon your code and precisely the thing that is causing the accident, so I will not conjecture about a specific arrangement.
To the extent of recognizing server mistake reaction codes, (for example, 404), that is more explicit to WebKit. I accept that you’re utilizing UIWebView on iPhone, and you’ve most likely seen that none of the essential strategies bring mistakes back. This is because it utilizes an agent model to report progress or blunders non concurrently. (It seems OK since you don’t need your UI code to be helpless before a lethargic stacking (or non-existent) website page. To be told of such blunders, there are a couple of steps.
Embrace the UIWebViewDelegate convention, generally in the very class that will begin the website page load for accommodation. Set that article as the representative of the UIWebView occurrence. (It has a representative property, so you can utilize something like either review. delegate = self or [uiView setDelegate: self] dependent on what you like.)
Carry out the webView:didFailLoadWithError: strategy in that class. (You can be informed when the heap wrapping up by carrying out webViewDidFinishLoad: also.) This is the place where you incorporate the rationale of what ought to happen when a blunder happens. I didn’t see any definite documentation on the substance of specific mistakes given back through this agent technique, yet it’s a standard NSError item, and I suggest looking at the substance by calling its strategies, for example, – localized description and – userInfo.
- MyClass.h
- @interface MyClass : NSObject
- {
- IBOutlet UIWebView* myWebView;
- }
- -(void)webView:(UIWebView*)webView didFailLoadWithError:(NSError *)error;
- @end
- MyClass.m
- @execution MyClass
- – (id) init {
- if ((self = [super init]) == nothing)
- bring nothing back;
- // introduce myWebView
- myWebView.delegate = self;
- bring self back;
- }
- – (void) webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error {
- …
- }
- @end
How to implement:
Here is some example code with #import proclamations rejected for quickness.
Dealing with Exceptions:
The exemption taking care of instruments accessible to Objective-C projects is a successful method of managing extraordinary conditions. They decouple the discovery and treatment of these conditions and robotize the spread of the exemption from the place of recognition with the result of taking care of. Thus, your code can be a lot cleaner, more straightforward to compose accurately, and simpler to keep up with.
- @attempt — Defines a square of code that is a special case taking care of space: code that might conceivably toss an exemption.
- @get() — Defines a square containing code for taking care of the special case tossed in the @try block. The boundary of @catch is the exemption object tossed locally; this is generally an NSException object, however can be different sorts of items, like NSString objects.
- @at long last — Defines a square of related code that is accordingly executed whether or not a special case is tossed.
- @toss — Throws an exemption; this mandate is practically indistinguishable in conduct to the raise strategy for NSException.
Dealing with Exceptions Using Compiler Directives
Compiler support for exemptions depends on four compiler orders:
You generally toss NSException objects, yet are not restricted to them. For more data about @throw, see Throwing Exceptions. The @try, @catch, and @finally orders comprise a control structure. The part of code between the supports in @try is the special case dealing with the area; the code in a @catch block is a neighborhood exemption overseer; the @finally square of code is a typical “housekeeping” segment. In Figure 1, the ordinary progression of program execution is set apart by the dark bolt; the code inside the nearby exemption overseer is executed provided that a special case is tossed—either by the neighborhood special case dealing with space or one further down the call grouping. The tossing (or raising) of a special case causes program control to leap to the principal executable line of the neighborhood exemption overseer. Later the special case is dealt with, control “fails to work out” to the @finally block; on the off chance that no exemption is tossed, control bounces from the @try square to the @finally block.
Flow of special case taking care of utilizing compiler orders:
Stream of special case taking care of utilizing compiler orders
Where and how a special case is taken care of relies upon the setting where the exemption was raised (albeit most exemptions in many projects go uncaught until they arrive at the high-level overseer introduced by the common NSApplication or UIApplication object). As a general rule, a special case object is tossed (or raised) inside the space of an exemption overseer. Even though you can toss an exemption straightforwardly inside a nearby special case taking care of the area, an exemption is more probable tossed (through @throw or raise) in a roundabout way from a strategy summoned from the space. Regardless of how somewhere down in a call arrangement the exemption is tossed, execution leaps to the nearby special case controller (expecting there are no interceding special case overseers, as examined in Nesting Exception Handlers). Thusly, special cases raised at a low level can be gotten at an undeniable level.
Posting 1 outlines how you may utilize the @try, @catch, and @finally compiler orders. In this model, the @catch block handles any special case tossed lower in the calling succession as a result of the setValue:forKeyPath: message by setting the impacted property to nothing all things being equal. The message in the @finally block is sent whether or not a special case is tossed.
Posting 1 Handling an exemption utilizing compiler orders
– (void)endSheet:(NSWindow *)sheet
{
BOOL achievement = [predicateEditorView commitEditing];
in the event that (achievement == YES) {
@attempt {
[treeController setValue:[predicateEditorView predicate] forKeyPath:@”selection.predicate”];
}
@get ( NSException *e ) {
[treeController setValue:nil forKeyPath:@”selection.predicate”];
}
@at long last {
[NSApp endSheet:sheet];
}
}
}
One method for dealing with exemptions is to “advance” them to blunder messages that either illuminate clients or solicitation their intercession. You can change over an exemption into an NSError item and afterward present the data in the mistake object to the client on an alarm board. In OS X, you could likewise hand this item over to the Application Kit’s blunder taking care of the system for a show to clients. You can likewise return them in a roundabout way in techniques that incorporate a mistake boundary. Posting 2 shows an illustration of the last option in an Automator activity’s execution of run with input:formation:error: (for this situation the blunder boundary is a pointer to an NSDictionary object rather than an NSError object).
Posting 2 Converting a special case into a blunder
– (id)runWithInput:(id)input fromAction:(AMAction *)anAction error:(NSDictionary **)errorInfo {
NSMutableArray *output = [NSMutableArray array];
NSString *actionMessage = nothing;
NSArray *recipes = nothing;
NSArray *summaries = nothing;
// other code here…
@attempt {
on the off chance that (managedObjectContext == nothing) {
actionMessage = @”accessing client formula library”;
[self initCoreDataStack];
}
actionMessage = @”finding plans”;
plans = [self recipesMatchingSearchParameters];
actionMessage = @”generating formula synopses”;
synopses = [self summariesFromRecipes:recipes];
}
@get (NSException *exception) {
NSMutableDictionary *errorDict = [NSMutableDictionary dictionary];
[errorDict setObject:[NSString stringWithFormat:@”Error %@: %@”, actionMessage, [exception reason]]
forKey:OSAScriptErrorMessage];
[errorDict setObject:[NSNumber numberWithInt:errOSAGeneralError] forKey:OSAScriptErrorNumber];
*errorInfo = errorDict;
bring input back;
}
// other code here…
}
You can have a succession of @catch blunder taking care of squares. Each square handles an exemption object of an alternate kind. You should arrange this succession of @catch blocks from the most-explicit to the most un-explicit kind of exemption object (the most un-explicit sort being id), as displayed in Listing 3. This sequencing permits you to tailor the handling of exemptions as gatherings.
Posting 3 Sequence of exemption controllers
@attempt {
// code that tosses an exemption
…
}
@get (CustomException *ce) {//most explicit sort
// handle special case ce
…
}
@get (NSException *ne) {//less explicit sort
// do whatever recuperation is essential at this level
…
// rethrow the special case so it’s dealt with at a more significant level
@toss;
}
@get (id ue) {//least explicit sort
// code that handles this special case
}
@at last {
// perform assignments vital whether or not exemption happened
…
}
Exception Handling and Memory Management:
Using the exception-handling directives of Objective-C can complicate memory management, but with a little common sense, you can avoid the pitfalls. To see how, let’s begin with the simple case: a method that, for the sake of efficiency, creates an object, uses it and then releases it explicitly:
– (void)doSomething {
NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:0];
[self doSomethingElse:anArray];
[anArray release];
}
The problem here is obvious: If they do something else: method throws an exception there is a memory leak. But the solution is equally obvious: Move the release to a @finally block:
– (void)doSomething {
NSMutableArray *anArray = nil;
array = [[NSMutableArray alloc] initWithCapacity:0];
@try {
[self doSomethingElse:anArray];
}
@finally {
[anArray release];
}
- – (void)doSomething {
- id savedException = nil;
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- NSMutableArray *anArray = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
- @try
- {
- [self doSomethingElse:anArray];
- }
- @catch (NSException *theException) {
- savedException = [theException retain];
- @throw;
- }
- @finally {
- [pool release];
- [savedException autorelease];
- }
- }
This pattern of using @try…@finally to release objects involved in an exception applies to other resources as well. If you have malloc’d block of memory or open file descriptors, @finally is a good place to free those; it’s also the ideal place to unlock any locks you’ve acquired.
Another, more subtle memory-management problem is over-releasing an exception object when there are internal autorelease pools. Almost all NSException objects (and other types of exception objects) are created autorelease, which assigns them to the nearest (in scope) autorelease pool. When that pool is released, the exception is destroyed. A pool can be either released directly or as a result of an autorelease pool further down the stack (and thus further out in scope) being popped (that is, released).
Consider this method:
– (void)doSomething {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *anArray = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
[self doSomethingElse:anArray];
[pool release];
}
This code appears to be sound; if the do something else: message results in a thrown exception, the local autorelease pool will be released when a lower (or outer) autorelease pool on the stack is popped. But there is a potential problem. As explained in Throwing Exceptions, a re-thrown exception causes its associated @finally block to be executed as an early side effect. If an outer autorelease pool is released in a @finally block, the local pool could be released before the exception is delivered, resulting in a “zombie” exception.
There are several ways to resolve this problem. The simplest is to refrain from releasing local autorelease pools in @finally blocks. Instead, let a pop of a deeper pool take care of releasing the pool holding the exception object. However, if no deeper pool is ever popped as the exception propagates up the stack, the pools on the stack will leak memory; all objects in those pools remain unreleased until the thread is destroyed.
An alternative approach would be to catch any thrown exception, retain it, and rethrow it. Then, in the @finally block, release the autorelease pool and autorelease the exception object. Listing 4 shows how this might look in code.
Listing 4 Releasing an autorelease pool containing an exception object
Doing this retains the thrown exception across the release of the interior autorelease pool—the pool the exception was put into on its way out of doSomethingElse:—and ensures that it is autorelease in the next autorelease pool outward to it in scope (or, in another perspective, the autorelease pool below it on the stack). For things to work correctly, the release of the interior autorelease pool must occur before the retained exception object is autorelease.
Conclusion:
In each block, you do not need to supply handlers for every type of exception. Handlers are only required for exceptions that must be handled specifically within a block.If a block fails to handle a specific exception, execution moves on to the next block that contains it (or the code that called the block), with the exception remaining raised. This procedure is repeated with more broad scope until either execution reaches the application’s outermost scope or an exception is handled by a block at some level.As a result, your exception handling code might be nested. That is, nested blocks can be used to provide local handling for certain exceptions that overrides the surrounding block’s behaviour.