https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Exceptions/Tasks/HandlingExceptions.html#//apple_ref/doc/uid/20000059-SW1

Handling Exceptions

The exception handling mechanisms available to Objective-C programs are effective ways of dealing with exceptional conditions. They decouple the detection and handling of these conditions and automate the propagation of the exception from the point of detection to the point of handling. As a result, your code can be much cleaner, easier to write correctly, and easier to maintain.

Handling Exceptions Using Compiler Directives

Compiler support for exceptions is based on four compiler directives:

  • @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 NSExceptionobjects, but are not limited to them. For more information about @throw, see Throwing Exceptions.

Important: Although you can throw and catch objects other than NSException objects, the Cocoa frameworks themselves might only catch NSExceptionobjects for some conditions. So if you throw other types of objects, the Cocoa handlers for that exception might not run, with undefined results. (Conversely, non-NSException objects that you throw could be caught by some Cocoa handlers.) For these reasons, it is recommended that you throw NSException objects only, while being prepared to catch exception objects of all types.

The @try@catch, and @finally directives constitute a control structure. The section of code between the braces in @try is the exception handling domain; the code in a @catch block is a local exception handler; the @finally block of code is a common “housekeeping” section. In Figure 1, the normal flow of program execution is marked by the gray arrow; the code within the local exception handler is executed only if an exception is thrown—either by the local exception handling domain or one further down the call sequence. The throwing (or raising) of an exception causes program control to jump to the first executable line of the local exception handler. After the exception is handled, control “falls through” to the @finally block; if no exception is thrown, control jumps from the @try block to the @finally block.

Figure 1  Flow of exception handling using compiler directives

Where and how an exception is handled depends on the context where the exception was raised (although most exceptions in most programs go uncaught until they reach the top-level handler installed by the shared NSApplication or UIApplication object). In general, an exception object is thrown (or raised) within the domain of an exception handler. Although you can throw an exception directly within a local exception handling domain, an exception is more likely thrown (through @throw or raise) indirectly from a method invoked from the domain. No matter how deep in a call sequence the exception is thrown, execution jumps to the local exception handler (assuming there are no intervening exception handlers, as discussed in Nesting Exception Handlers). In this way, exceptions raised at a low level can be caught at a high level.

Listing 1 illustrates how you might use the @try@catch, and @finally compiler directives. In this example, the @catch block handles any exception thrown lower in the calling sequence as a consequence of the setValue:forKeyPath: message by setting the affected property to nil instead. The message in the @finally block is sent whether an exception is thrown or not.

Listing 1  Handling an exception using compiler directives

  1. - (void)endSheet:(NSWindow *)sheet
  1. {
  1. BOOL success = [predicateEditorView commitEditing];
  1. if (success == YES) {
 
  1. @try {
  1. [treeController setValue:[predicateEditorView predicate] forKeyPath:@"selection.predicate"];
  1. }
 
  1. @catch ( NSException *e ) {
  1. [treeController setValue:nil forKeyPath:@"selection.predicate"];
  1. }
 
  1. @finally {
  1. [NSApp endSheet:sheet];
  1. }
  1. }
  1. }

One way to handle exceptions is to “promote” them to error messages that either inform users or request their intervention. You can convert an exception into an NSError object and then present the information in the error object to the user in an alert panel. In OS X, you could also hand this object over to the Application Kit’s error-handling mechanism for display to users. You can also return them indirectly in methods that include an error parameter. Listing 2shows an example of the latter in an Automator action’s implementation of runWithInput:fromAction:error: (in this case the error parameter is a pointer to an NSDictionary object rather than an NSError object).

Listing 2  Converting an exception into an error

  1. - (id)runWithInput:(id)input fromAction:(AMAction *)anAction error:(NSDictionary **)errorInfo {
 
  1. NSMutableArray *output = [NSMutableArray array];
  1. NSString *actionMessage = nil;
  1. NSArray *recipes = nil;
  1. NSArray *summaries = nil;
 
  1. // other code here....
 
  1. @try {
  1. if (managedObjectContext == nil) {
  1. actionMessage = @"accessing user recipe library";
  1. [self initCoreDataStack];
  1. }
  1. actionMessage = @"finding recipes";
  1. recipes = [self recipesMatchingSearchParameters];
  1. actionMessage = @"generating recipe summaries";
  1. summaries = [self summariesFromRecipes:recipes];
  1. }
  1. @catch (NSException *exception) {
  1. NSMutableDictionary *errorDict = [NSMutableDictionary dictionary];
  1. [errorDict setObject:[NSString stringWithFormat:@"Error %@: %@", actionMessage, [exception reason]] forKey:OSAScriptErrorMessage];
  1. [errorDict setObject:[NSNumber numberWithInt:errOSAGeneralError] forKey:OSAScriptErrorNumber];
  1. *errorInfo = errorDict;
  1. return input;
  1. }
 
  1. // other code here ....
  1. }

Note: For more on the Application Kit’s error-handling mechanisms, see Error Handling Programming Guide. To learn more about Automator actions, see Automator Programming Guide.

You can have a sequence of @catch error-handling blocks. Each block handles an exception object of a different type. You should order this sequence of @catch blocks from the most-specific to the least-specific type of exception object (the least specific type being id), as shown in Listing 3. This sequencing allows you to tailor the processing of exceptions as groups.

Listing 3  Sequence of exception handlers

  1. @try {
  1. // code that throws an exception
  1. ...
  1. }
  1. @catch (CustomException *ce) { // most specific type
  1. // handle exception ce
  1. ...
  1. }
  1. @catch (NSException *ne) { // less specific type
  1. // do whatever recovery is necessary at his level
  1. ...
  1. // rethrow the exception so it's handled at a higher level
  1. @throw;
  1. }
  1. @catch (id ue) { // least specific type
  1. // code that handles this exception
  1. ...
  1. }
  1. @finally {
  1. // perform tasks necessary whether exception occurred or not
  1. ...
  1. }

Note: You cannot use the setjmp and longjmp functions if the jump entails crossing an @try block. Since the code that your program calls may have exception-handling domains within it, avoid using setjmp and longjmp in your application. However, you may use goto or return to exit an exception handling domain.

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:

  1. - (void)doSomething {
  1. NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:0];
  1. [self doSomethingElse:anArray];
  1. [anArray release];
  1. }

The problem here is obvious: If the doSomethingElse: method throws an exception there is a memory leak. But the solution is equally obvious: Move the release to a @finally block:

  1. - (void)doSomething {
  1. NSMutableArray *anArray = nil;
  1. array = [[NSMutableArray alloc] initWithCapacity:0];
  1. @try {
  1. [self doSomethingElse:anArray];
  1. }
  1. @finally {
  1. [anArray release];
  1. }
  1. }

This pattern of using @try...@finally to release objects involved in an exception applies to other resources as well. If you have malloc’d blocks 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 autoreleased, 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:

  1. - (void)doSomething {
  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1. NSMutableArray *anArray = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
  1. [self doSomethingElse:anArray];
  1. [pool release];
  1. }

This code appears to be sound; if the doSomethingElse: 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

  1. - (void)doSomething {
  1. id savedException = nil;
  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1. NSMutableArray *anArray = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
  1. @try {
  1. [self doSomethingElse:anArray];
  1. }
  1. @catch (NSException *theException) {
  1. savedException = [theException retain];
  1. @throw;
  1. }
  1. @finally {
  1. [pool release];
  1. [savedException autorelease];
  1. }
  1. }

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 autoreleased 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 autoreleased.

Handling Exceptions的更多相关文章

  1. Handling Errors and Exceptions

    http://delphi.about.com/od/objectpascalide/a/errorexception.htm Unfortunately, building applications ...

  2. Python Tutorial 学习(八)--Errors and Exceptions

    Python Tutorial 学习(八)--Errors and Exceptions恢复 Errors and Exceptions 错误与异常 此前,我们还没有开始着眼于错误信息.不过如果你是一 ...

  3. [转]java-Three Rules for Effective Exception Handling

    主要讲java中处理异常的三个原则: 原文链接:https://today.java.net/pub/a/today/2003/12/04/exceptions.html Exceptions in ...

  4. 写出简洁的Python代码: 使用Exceptions(转)

    add by zhj: 非常好的文章,异常在Python的核心代码中使用的非常广泛,超出一般人的想象,比如迭代器中,当我们用for遍历一个可迭代对象时, Python是如何判断遍历结束的呢?是使用的S ...

  5. [译]The Python Tutorial#8. Errors and Exceptions

    [译]The Python Tutorial#Errors and Exceptions 到现在为止都没有过多介绍错误信息,但是已经在一些示例中使用过错误信息.Python至少有两种类型的错误:语法错 ...

  6. 异步编程错误处理 ERROR HANDLING

    Chapter 16, "Errors and Exceptions," provides detailed coverage of errors and exception ha ...

  7. Java exception handling best practices--转载

    原文地址:http://howtodoinjava.com/2013/04/04/java-exception-handling-best-practices/ This post is anothe ...

  8. Error handling in Swift does not involve stack unwinding. What does it mean?

    Stack unwinding is just the process of navigating up the stack looking for the handler. Wikipedia su ...

  9. C++的性能C#的产能?! - .Net Native 系列《三》:.NET Native部署测试方案及样例

    之前一文<c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATIVE初窥> 获得很多朋友支持和鼓励,也更让我坚定做这项技术的推广者,希望能让更多的朋友了解这项技术,于是先从官方 ...

随机推荐

  1. express+vue-cli实现前后端分离交互小例

    准备工作 1.Express 应用生成器 npm install express-generator -g 2.vue-cli手脚架 npm install -g vue-cli 3.项目结构 . ├ ...

  2. Eclipse项目中乱码问题的解决办法

    一.产生的原因: 1.Http协议进行通信的时候是基于请求和响应的,传输的内容我们称之为报文! 2.Http协议会按照一定的规则将报文编码,然后在读取的时候再使用响应的解码格式进行解码! 3.这个一定 ...

  3. 帝都Day4(2)——数据结构

    黄姓dalao is coming! 一.栈: 基本常识略. 例题:铁轨 模拟,O(n), Usaco2006 Nov 题目略 做法:单调栈(续命栈?) n//数量 a[]//奶牛 for(int i ...

  4. CSS十一问——好奇心+刨根问底=CSSer

    最近有时间,想把酝酿的几篇博客都写出来,今天前端小学生带着10个问题,跟大家分享一下学习CSS的一些体会,我觉得想学好CSS,必须保持一颗好奇心和刨根问底的劲头,而不是复制粘贴,得过且过.本人能力有限 ...

  5. CentOS下ganglia监控部署

    第一步:CentOS环境准备 1.yum -y install apr-devel apr-util check-devel cairo-devel pango-devel libxml2-devel ...

  6. BZOJ 1053 [HAOI2007]反素数ant 神奇的约数

    本蒟蒻终于开始接触数学了...之前写的都忘了...忽然想起来某神犇在几个月前就切了FWT了... 给出三个结论: 1.1-N中的反素数是1-N中约数最多但是最小的数 2.1-N中的所有数的质因子种类不 ...

  7. Uva1149

    每个bin最多只能放两个,所以最佳的贪心策略是从大的开始放,如果有空间放第二个,尽量放最大的. #include <bits/stdc++.h> using namespace std; ...

  8. python大战机器学习——模型评估、选择与验证

    1.损失函数和风险函数 (1)损失函数:常见的有 0-1损失函数  绝对损失函数  平方损失函数  对数损失函数 (2)风险函数:损失函数的期望      经验风险:模型在数据集T上的平均损失 根据大 ...

  9. leetcoe--47. Permutations II

    1.问题描述 Given a collection of numbers that might contain duplicates, return all possible unique permu ...

  10. 完美解决百度地图MarkerClusterer 移动地图时,Marker 的Label 丢失的问题

    这篇文章来自http://www.cnblogs.com/jicheng1014   不好意思,那些rss 站太生猛了. 先吐槽一下百度地图的开发者,其实这个问题我绝对不是第一个遇到的人 很多人把这个 ...