Exception Programming Topics

959 阅读20分钟

Exception Programming Topics

Cocoa中, 使用NSException类表示异常, 是一个可以中断正常执行程序的条件

重要 应保留对编程的异常使用或意外的运行时错误,例如越界集合访问,尝试使不可变对象发生突变,发送无效消息以及与窗口服务器的连接断开, 通常在创建应用程序时(而不是在运行时)会处理这类带有异常的错误

包含异常处理的代码段(也可能来自三方库), 可以在Cocoa应用中正常使用它们, 但是应该确保任何预期的运行时异常都不会从这些子系统中逸出并最终出现在调用者的代码中, 例如:解析库可能在内部使用异常来指示问题, 并允许快速退出可能是深度递归的解析状态, 此时应该注意在库的顶层捕获此类异常, 并将其转换为适当的返回码或状态

建议使用错误对象(NSError)和Cocoa错误传递机制来代替异常以在Cocoa应用程序中传达预期的错误, 详情查看Error Handling Programming Guide

Exceptions and the Cocoa Frameworks

Cocoa中, 使用NSException类表示异常, 是Foundation框架的一部分, 此类的方法允许创建异常对象, 使用它们抛出异常, 并获取与异常相关的调用返回地址, NSException对象的属性如下

  • 一个name -- 用来唯一标识异常的短字符串, 名称为必填项
  • 一个reason -- 较长的字符串其中包含可读可理解的异常原因, 原因为必填项
  • 一个可选的字典userInfo -- 用于向异常处理程序提供特定于应用程序的数据
    • 例如如果方法的返回值导致引发异常, 则可以通过userInfo将返回值传递给异常处理程序

可以将信息提取到异常对象中, 并在适当时使用NSError对象在提示对话框中呈现给用户

Cocoa框架要求所有异常都是NSException或其子类的实例, 不要抛出他类型的对象

Cocoa框架通常不是异常安全的, 通常的模式是:异常仅保留给程序员错误信息, 并且捕获此类异常的程序应在不久后退出

Handling Exceptions

Objective-C程序异常处理机制是处理异常情况的有效方法, 它们将这些条件的检测和处理解耦,并使异常从检测点到处理点自动进行, 因此编写代码可以更简洁、更容易正确编写并且更易于维护

Handling Exceptions Using Compiler Directives

编译器对异常的支持基于四个编译器指令

  • @try
    • 定义一个代码块, 它是异常处理域:可能引发异常的代码
  • @catch()
    • 定义一个包含用于处理@try块中引发的异常的代码的块
    • @catch参数是在本地引发异常对象, 通常是一个NSException对象, 当然也可以是其他类型的对象, 如NSString对象
  • @finally
    • 定义一个相关代码块, 该块随后执行无论是否引发异常
  • @throw
    • 抛出异常, 此指令在行为上与NSException的raise法几乎相同
    • 通常抛出NSException对象, 但不要仅仅局限于它们

重要 虽然可以抛出其他类型的对象, 但在Cocoa框架中, 某些条件下只能抛出NSException对象, 因此如果引发其他类型的对象, 则该异常的Cocoa处理程序可能不会运行,且结果显示是未定义(相反抛出的非NSException对象可能会被某些Cocoa处理程序捕获), 出于这些原因建议您只抛出NSException对象

@try@catch@finally指令构成控制结构, @try大括号之间的代码部分是异常处理域, @catch块中的代码是本地异常处理程序, @finally代码块是一个常见的"内部管理"部分

见下图, 程序执行的正常流用灰色箭头标记, 仅当引发异常时才会执行本地异常处理程序中的代码

  • 抛出异常会导致程序控制跳转到本地异常处理程序部分开始执行
  • 处理异常后, 控制进入到@finally
  • 如果未引发异常则控件从@try块跳转到@finally

Flow of exception handling using compiler directives

何时何地处理异常依赖于抛出异常的上下文(通常不处理, 则会抛出到最顶层的由NSApplication对象或者UIApplication对象设置的异常捕获), 通常异常对象在异常处理程序的域中抛出, 尽管可以直接在本地异常处理域中引发异常, 但更有可能从域中调用的方法间接引发异常(通过@throw抛出), 无论调用序列有多深抛出的异常, 都会执行跳转到本地异常处理程序, 这样在低层抛出的异常可以在高层捕获

示例

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

处理异常的一种方法是将它们"升级"到错误消息, 这些错误消息要么通知用户要么请求他们的干预,可以将异常转换为NSError对象然后在alter面板中向用户显示错误对象中的信息, 在 OS X 中还可以将此对象交给应用程序工具包的错误处理机制以便向用户显示, 还可以在包含错误参数的方法中间接返回它们

Converting an exception into an error

- (id)runWithInput:(id)input fromAction:(AMAction *)anAction error:(NSDictionary **)errorInfo {
 
    NSMutableArray *output = [NSMutableArray array];
    NSString *actionMessage = nil;
    NSArray *recipes = nil;
    NSArray *summaries = nil;
 
    // other code here....
 
    @try {
        if (managedObjectContext == nil) {
            actionMessage = @"accessing user recipe library";
            [self initCoreDataStack];
        }
        actionMessage = @"finding recipes";
        recipes = [self recipesMatchingSearchParameters];
        actionMessage = @"generating recipe summaries";
        summaries = [self summariesFromRecipes:recipes];
    }
    @catch (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;
        return input;
    }
 
    // other code here ....
}

注意 不能使用setjmp, longjmp函数执行在@try代码块的跳转逻辑, 由于程序调用的代码可能具有异常处理域, 也要避免在应用程序中使用 setjmplongjmp, 但是可以使用 gotoreturn退出异常处理域

Exception Handling and Memory Management

使用 Objective-C 的异常处理指令会使内存管理复杂化, 但有一点常识就可以避免陷阱

先看一个示例

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

这里的问题很明显: 如果doSomethingElse:方法抛出异常将会导致内存泄漏, 但解决方案同样显而易见将release移动到@finally

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

这种使用@try...@finally来释放异常中涉及的对象的模式也适用于其他资源, 如果您有malloc'的代码块的内存或打开的文件描述符, @finally是一个很好释放资源的地方, 这也是解锁获得的任何锁的理想场所

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

另一个更微妙的内存管理问题是当存在内部自动释放池时, 过度释放异常对象. 几乎所有NSException对象都添加到了自动释放池, 会添加到作用域内最近的那个池中, 释放该池时, 异常将销毁, 自动释放池可以直接释放, 也可能会是所在栈自动弹出释放

请考虑以下方法

- (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 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

此代码似乎看起来良好, 但是如果doSomethingElse:方法抛出一个异常, 当堆栈上的较低(或外部)自动释放池弹出时, 将释放本地自动释放池, 这就会有一个潜在的问题,关于re-thrown再次抛出异常, 如果在@finally块中释放外部自动释放池, 在异常被抛出之前, 内部的自动释放池被释放了, 会造成僵尸异常(看原文理解, 不对的地方感谢指正, 要写个示例验证下)

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

有几种方法可以解决此问题, 最简单的方法是避免在@finally块中释放内部自动释放池, 在内部自动释放池释放的时候持有异常对象, 然而异常会一直向上传递, 导致自动释放池没有释放, 内存泄漏, 那么池中的对象都会被持有, 直到所在线程被释放(看原文理解, 不对的地方感谢指正)

另一种方法是捕获任何抛出的异常保留它然后重新抛出它, 然后在@finally块中释放自动释放池并自动释放异常对象, 见下面示例

- (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];
    }
}

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.

疑问ARC的异常内存管理怎么处理?

Throwing Exceptions

程序检测到异常后, 必须将异常抛出到处理它的代码块, 此代码称为异常处理程序

抛出异常要做的事情, 二选一

  • 使用NSException对象作为@throw编译器指令的参数
  • NSException对象调用raise方法
NSException* myException = [NSException
        exceptionWithName:@"FileNotFoundException"
        reason:@"File Not Found on System"
        userInfo:nil];
@throw myException;
// [myException raise]; /* equivalent to above directive */

两者区别 @throw可以采用其他类型的对象作为其参数

通常在异常处理域中抛出异常, 该域是由@try编译器指令标记的代码块

在异常处理域中, 可以通过向NSException对象发送另一个raise消息或另一@throw指令, 可将异常往上抛出

@try {
    NSException *e = [NSException
        exceptionWithName:@"FileNotFoundException"
        reason:@"File Not Found on System"
        userInfo:nil];
    @throw e;
}
@catch(NSException *e) {
    @throw; // rethrows e implicitly
}

Nesting Exception Handlers

异常处理程序可以嵌套, 以便本地异常处理程序和任意数量的包含异常处理程序可以处理内部域中抛出的异常. 此设计允许由代码处理异常, 尽管该异常与实际生成异常的代码更远, 但可能对导致异常的条件有更多的了解

Nested Exception Handlers With Compiler Directives

为了理解如何调用使用编译器指令定义嵌套异常处理程序, 请看示例

@try {
    // ...
    if (someError) {
        NSException *theException = [NSException exceptionWithName:MyAppException reason:@"Some error just occurred!" userInfo:nil];
        @throw theException;
    }
}
@catch (NSException *exception) {
    if ([[exception name] isEqualToString:MyAppException]) {
        NSRunAlertPanel(@"Error Panel", @"%@", @"OK", nil, nil,
                localException);
    }
    @throw; // rethrow the exception
}
@finally {
    [self cleanUp];
}

在此代码中, 在本地句柄的末尾再次抛出异常(exception)

Control flow with nested exception handlers—using directives

Control flow with nested exception handlers—using directives

在方法3的域中抛出的异常导致执行跳转到其本地异常处理程序. 在典型的应用程序中, 此异常处理程序查询异常对象以确定异常的性质. 本地处理程序可以处理它识别的异常类型, 然后可以重新抛出异常对象, 以将异常通知传递给嵌套在其上方的处理程序. 即方法2中的处理程序. 但是在调用下一个外部异常处理程序之前, 将执行与本地异常处理程序关联的@finally块中的代码(这对内存管理有影响)

重新抛出的异常出现在下一个更高层的处理程序中, 就像在其自身的异常处理域中引发了初始异常一样. 方法2的异常处理程序可以再次处理该异常, 并将异常重新抛出给方法1的异常处理程序. 在方法2的@finally块完成其任务之前, 方法1的处理程序不会收到重新抛出的异常. 最后方法1的处理程序将异常抛出. 因为方法1上方没有异常处理域, 所以该异常将传递给未捕获的异常处理程序

Uncaught Exceptions

如果未捕获到异常,则该异常将被称为未捕获的异常处理程序的函数拦截, 未捕获的异常处理程序始终会导致程序退出, 但在此发生之前可能会执行一些任务

默认的未捕获异常处理程序在退出程序之前将消息记录到控制台. 在OS X上, 如果从shell启动应用程序, 则日志消息将发送到"终端"窗口

可以使用NSSetUncaughtExceptionHandler函数自定义设置为未捕获的异常处理程序, 可以使用NSGetUncaughtExceptionHandler函数获取当前未捕获的异常处理程序

示例


void UncaughtExceptionHandler(NSException *exception) {
    /**
     *  获取异常崩溃信息
     */
    NSArray *callStack = [exception callStackSymbols];
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];
    NSString * dateStr = [formatter stringFromDate:[NSDate date]];
    
    // 发送报告啥的....
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  

    // 注意和三方崩溃统计的冲突  
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
    
    return YES;
}

注意 由于全局应用程序对象捕获了所有此类异常,因此Cocoa应用程序主线程上的异常通常不会上升到未捕获的异常处理程序级别

Predefined Exceptions

Cocoa预定义了一些通用的异常名称, 以标识可以在代码中处理的异常, 可以抛出和重新抛出异常. 还可以创建和使用自定义异常名称. 通用异常名称是在NSException.h中定义的字符串常量

  • NSGenericException
  • NSRangeException
  • NSInvalidArgumentException
  • NSInternalInconsistencyException
  • NSObjectInaccessibleException
  • NSObjectNotAvailableException
  • NSDestinationInvalidException
  • NSPortTimeoutException
  • NSInvalidSendPortException
  • NSInvalidReceivePortException
  • NSPortSendException
  • NSPortReceiveException

除通用异常名称外, Cocoa的某些子系统还定义了自己的异常名称, 例如NSInconsistentArchiveExceptionNSFileHandleOperationException

通过将异常的名称与这些预定义的名称进行比较, 可以在异常处理程序中识别捕获的异常. 然后可以处理异常, 或者是不感兴趣的异常则重新抛出, 请注意所有预定义的例外均以前缀NS开头. 因此在创建新的异常名称时, 应避免使用相同的前缀

Controlling a Program’s Response to Exceptions

本文档介绍了一些用户默认设置以及Exception Handling框架的API,您可以使用它们来控制应用程序的行为以响应某些类型的错误

Cocoa项目中使用异常处理框架的服务, 将/System/Library/Frameworks中的ExceptionHandling.framework添加到Xcode项目中, 还将以下导入指令插入使用该框架的类的头文件或实现文件中

#import <ExceptionHandling/NSExceptionHandler.h>

重要异常处理框架在iOS上不可用

通过异常处理框架设置的未捕获异常处理程序, 可以使下面描述的服务成为可能. 但是通过 NSSetUncuncexceptionHandler函数设置自定义未捕获的异常处理程序, 这些服务将不可用

Application Errors

某些类型的应用程序错误通常会导致Cocoa应用程序突然退出. 可以使用用户默认值 NSExceptionHandlingMask来控制此行为(仅适用于基于应用程序工具包的应用程序)用于大多数类的三个常见错误

  • 未捕获的NSExceptions
  • 系统级的异常(例如无效的内存访问)
  • Objective-C运行时错误(例如像已释放的对象发送消息)

对于这些错误类型可以将NSExceptionHandlingMask设置为执行以下操作之一

  • 在发生此类错误时将描述性日志和堆栈跟踪打印到控制台
  • 以防止导致突然终止的方式处理错误
  • 执行上述两项操作

通过添加与要记录或处理的错误类型对应的值来构造掩码

异常处理常量和默认值

Type of Action Type of Action Value for defaults
Log uncaught NSExceptions NSLogUncaughtExceptionMask 1
Handle uncaught NSExceptions NSHandleUncaughtExceptionMask 2
Log system-level exceptions NSLogUncaughtSystemExceptionMask 4
Handle system-level exceptions NSHandleUncaughtSystemExceptionMask 8
Log runtime errors NSLogUncaughtRuntimeErrorMask 16
Handle runtime errors NSHandleUncaughtRuntimeErrorMask 32

因此如果在命令行(在终端应用程序中)输入以下内容

defaults write NSGlobalDomain NSExceptionHandlingMask 63

会打印描述所有应用程序中所有未捕获异常、系统级异常和运行时错误的日志记录和处理行为

异常处理常量中的"handle(句柄)"一词具有特定的含义, 具体取决于异常的类型. 异常处理框架通过将系统级异常和运行时错误转换为NSException对象来处理它们, 这些异常对象在userInfo字典中以NSStackTraceKey为key保存堆栈信息. 框架通过终止发生这些对象的线程来处理未捕获的NSException对象. Cocoa应用程序主线程上的异常由顶级处理程序捕获, 这些处理程序通常由应用程序工具包安装

可以使用setExceptionHandlingMask:异常处理框架的方法来设置获取相同的异常处理行为, 而不是NSExceptionHandlingMask用户默认值

对于应用程序和非应用程序Cocoa可执行文件, 针对异常处理框架链接并发送以下消息

[[NSExceptionHandler defaultExceptionHandler] setExceptionHandlingMask: aMask];

aMask参数是一个位掩码, 由bitwise-ORing(位或)上表中列出的常量组成

Debugging Aids

出于调试目的还可以使用相同的机制来报告NSException否则就会捕获. 还可以使用默认系统的NSExceptionHandlingMask属性来达到此目的, 也可以使用NSExceptionHandler类的"setExceptionHandlingMask:"方法. 下表列出了相关的常量和值

调试常量和默认值

Type of Action Constant Value for defaults
由NSApplication的顶级异常处理程序捕获的日志异常 NSLogTopLevelExceptionMask 64
处理NSApplication中顶级异常处理程序捕获的异常 NSHandleTopLevelExceptionMask 128
记录将在较低层捕获的异常 NSLogOtherExceptionMask 256
H处理将在较低层捕获的异常 NSHandleOtherExceptionMask 512

注意 嵌套异常处理域时, 记录异常, 使其进入最外两层, 在Cocoa应用程序的主线程, 这意味着是NSApp捕获的异常

在这些情况下, 捕获一个异常不仅仅只是userInfo字典里存放堆栈信息

请注意,捕获的异常应仅记录或处理用于调试,而不是在正常情况下处理. 因为这样做可能会生成大量输出或更改应用程序的正常行为

出于进一步的调试目的可以更改NSExceptionHandler处理的任何条件的处理行为. 以便改为停止应用程序附加调试器. 可以使用NSExceptionHangingMask用户默认值或将使用NSExceptionHandler类的setExceptionHangingMask:方法来控制

下表列出了有效的常量和默认值

Type of Action Constant Value for defaults
Hang for uncaught exceptions NSHangOnUncaughtExceptionMask 1
Hang for system-level exceptions NSHangOnUncaughtSystemExceptionMask 2
Hang for runtime errors NSHangOnUncaughtRuntimeErrorMask 4
Hang for top-level caught exceptions NSHangOnTopLevelExceptionMask 8
Hang for other caught exceptions NSHangOnOtherExceptionMask 16

Printing Symbolic Stack Traces

作为调试的辅助工具可以使用atos命令行实用程序将数字堆栈跟踪转换为符号形式

注意必须安装开发人员工具包才能安装atos程序。此外NSException类提供调用 callStackReturnAddresses, 可以使用类似atos的方式进行调试

无需在Xcode和终端shell之间切换, 该程序使用atos的代码, 将符号堆栈跟踪打印到控制台

示例 打印异常的符号回溯跟踪的方法

- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(unsigned int)mask
{
    [self printStackTrace:exception];
    return YES;
}
 
- (void)printStackTrace:(NSException *)e
{
    NSString *stack = [[e userInfo] objectForKey:NSStackTraceKey];
    if (stack) {
        NSTask *ls = [[NSTask alloc] init];
        NSString *pid = [[NSNumber numberWithInt:[[NSProcessInfo processInfo] processIdentifier]] stringValue];
        NSMutableArray *args = [NSMutableArray arrayWithCapacity:20];
 
        [args addObject:@"-p"];
        [args addObject:pid];
        [args addObjectsFromArray:[stack componentsSeparatedByString:@"  "]];
        // Note: function addresses are separated by double spaces, not a single space.
 
        [ls setLaunchPath:@"/usr/bin/atos"];
        [ls setArguments:args];
        [ls launch];
        [ls release];
 
    } else {
        NSLog(@"No stack trace available.");
    }
}

在此示例中, 代理在其方法exceptionHandler:shouldLogException:mask:中调用了printStackTrace:方法, 此时异常正在处理中, 但尚未导致调试的可执行文件的终止

NSExceptionHandler日志加上atos输出的内容

2006-08-21 12:18:19.727 ExceptionHandleTest[916] NSExceptionHandler has recorded the following exception:
NSInvalidArgumentException -- *** -[NSCFString count]: selector not recognized [self = 0x2a00c]
Stack trace:  0x9275c27b  0x92782fd7  0x9280b0be  0x9272f207  0x90a51ba1  0x0002995f  0x00023f81  0x00001ca6  0x00001bcd  0x00000001
__NSRaiseError (in Foundation)
+[NSException raise:format:] (in Foundation)
-[NSObject doesNotRecognizeSelector:] (in Foundation)
-[NSObject(NSForwardInvocation) forward::] (in Foundation)
__objc_msgForward (in libobjc.A.dylib)
-[ExceptionTest testException] (in ExceptionHandleTest) (ExceptionTest.m:31)
_main (in ExceptionHandleTest) (ExceptionHandleTest.m:10)
start (in ExceptionHandleTest)
start (in ExceptionHandleTest)
0x00000001 (in ExceptionHandleTest)

还有其他方法可以完成相同的结果. 例如, 打印符号堆栈跟踪的方法可能位于添加到 NSException的分类上, 而不是委托类的方法上

Exceptions in 64-Bit Executables

Objective-C运行时为64位可执行文件重新实现了异常机制, 以提供零成本的@try块以及与C++异常的互操作性

Zero-Cost @try Blocks

64位程序输入零成本@try块不会造成性能损失. 这不同于32位进程的机制, 32位该机制调用setjmp()并执行其他bookkeeping, 但是在64位可执行文件中抛出异常的开销要大得多. 为了获得64位的最佳性能, 仅在绝对必要时才应抛出异常

C++ Interoperability

In 64-bit processes, Objective-C exceptions (NSException) and C++ exception are interoperable. Specifically, C++ destructors and Objective-C @finally blocks are honored when the exception mechanism unwinds an exception. In addition, default catch clauses—that is, catch(...) and @catch(...)—can catch and rethrow any exception

On the other hand, an Objective-C catch clause taking a dynamically typed exception object (@catch(id exception)) can catch any Objective-C exception, but cannot catch any C++ exceptions. So, for interoperability, use @catch(...) to catch every exception and @throw; to rethrow caught exceptions. In 32-bit, @catch(...) has the same effect as @catch(id exception).

在64位进程中,Objective-C异常(NSException)和C ++异常是可互操作的, 注意C++的析构函数和Objective-C的@finally块在当异常机制解除异常时, 应当值得关注

另外默认的catch语句, 即catch(...)和@catch(...)可以捕获并抛出任何异常

另一方面Objective-C catch语句采用动态类型的异常对象(@catch(id exception))可以捕获任何Objective-C异常, 但无法捕获任何C++异常, 因此为了互操作性, 使用@catch(...)捕捉每个异常并@throw重新抛出捕获的异常. 在32位中, @catch(...)@catch(id exception)具有相同的效果

理解如有错误 望指正 转载请说明出处