exception GenericError {
string reason;
};
exception BadTimeVal extends GenericError {};
exception BadZoneName extends GenericError {};
@interface EXGenericError : ICEUserException
{
@private
NSString *reason_;
}
@property(nonatomic, retain) NSString *reason_;
// ...
@end
@interface EXBadTimeVal : EXGenericError
// ...
@end
@interface EXBadZoneName : EXGenericError
// ...
@end
Each Slice exception is mapped to an Objective‑C class. For each exception member, the corresponding class contains a private instance variable and a property. (Obviously, because
BadTimeVal and
BadZoneName do not have members, the generated classes for these exceptions also do not have members.)
In turn, EXGenericError derives from
ICEUserException:
@interface ICEException : NSException
‑(NSString* *)ice_name;
@end
@interface ICEUserException : ICEException
// ...
@end
@interface ICELocalException : ICEException
// ...
@end
Note that ICEUserException itself derives from
ICEException, which derives from
NSException. Similarly, run-time exceptions derive from a common base class
ICELocalException that derives from
ICEException, so we have the inheritance structure shown in
Figure 18.1:
ICEException provides a single method,
ice_name, that returns the Slice type ID of the exception with the leading
:: omitted. For example, the return value of
ice_name for our Slice
GenericError is
Example::GenericError.
As we mentioned on page 531, each data member of a Slice exception generates a corresponding Objective‑C property. Here is an example that extends our
GenericError with yet another exception:
exception GenericError {
string reason;
};
exception FileError extends GenericError {
string name;
int errorCode;
};
@interface EXGenericError : ICEUserException
{
@private
NSString *reason_;
}
@property(nonatomic, retain) NSString *reason_;
// ...
@end
@interface EXFileError : EXGenericError
{
@private
NSString *name_;
ICEInt errorCode;
}
@property(nonatomic, retain) NSString *name_;
@property(nonatomic, assign) ICEInt errorCode;
// ...
@end
This is exactly the same mapping as for structure members (see page 521), with one difference: the
name and
reason members map to
name_ and
reason_ properties, whereas—as for structures—
errorCode maps to
errorCode. The trailing underscore for
reason_ and
name_ prevents a name collision with the
name and
reason methods that are defined by
NSException: if you call the
name method, you receive the name that is stored by
NSException; if you call the
name_ method, you receive the value of the
name_ instance variable of
EXFileError:
@try {
// Do something that can throw ExFileError...
}
@catch(EXFileError *ex)
{
// Print the value of the Slice reason, name,
// and errorCode members.
printf("reason: %s, name: %s, errorCode: %d\n",
[ex.reason_ UTF8String],
[ex.name_ UTF8String],
ex.errorCode);
// Print the NSException name.
printf("NSException name: %s\n", [[ex name] UTF8String]);
}
Initialization of exceptions follows the same pattern as for structures (see page 522): each exception (apart from the inherited no-argument
init method) provides an
init method that accepts one argument for each data member of the exception, and two convenience constructors. For example, the generated methods for our
EXGenericError exception look as follows:
@interface EXGenericError : ICEUserException
// ...
‑(id) init:(NSString *)reason;
+(id) genericError;
+(id) genericError:(NSString *)reason;
@endif
If a user exception has no data members (and its base exceptions do not have data members either), only the inherited
init method and the no-argument convenience constructor are generated.
If an exception has a base exception with data members, its init method and convenience constructor accept one argument for each Slice data member, in base-to-derived order. For example, here are the methods for the
FileError exception we defined on
page 532):
@interface EXFileError : EXGenericError
// ...
‑(id) init:(NSString *)reason name_:(NSString *)name
errorCode:(ICEInt)errorCode;
+(id) fileError;
+(id) fileError:(NSString *)reason name_:(NSString *)name
errorCode:(ICEInt)errorCode;
@end
Note that init and the second convenience constructor accept three arguments; the first initializes the
EXGenericError base, and the remaining two initialize the instance variables of
EXFileError.
The Ice run time throws run-time exceptions for a number of pre-defined error conditions. All run-time exceptions directly or indirectly derive from
ICELocalException which, in turn, derives from
ICEException. (See
Figure 4.4 for an inheritance diagram.)
Catching ICEException catches both user and run-time exceptions.
For example, an ICEConnectTimeoutException can be handled as
ICEConnectTimeoutException,
ICETimeoutException,
ICELocalException,
ICEException, or
NSException.
You will probably have little need to catch run-time exceptions as their most-derived type and instead catch them as
ICELocalException; the fine-grained error handling offered by the remainder of the hierarchy is of interest mainly in the implementation of the Ice run time. Exceptions to this rule are
ICEFacetNotExistException (see
Chapter 34) and
ICEObjectNotExistException (see
Chapter 35), which you may want to catch explicitly.
ICELocalException provides two properties that return the file name and line number at which an exception was raised:
@interface ICELocalException : ICEException
{
// ...
@property(nonatomic, readonly) NSString* file;
@property(nonatomic, readonly) int line;
‑(id)init:(const char*)file line:(int)line;
+(id)localException:(const char*)file line:(int)line;
@end
The init method and the convenience constructor accept the file name and line number as arguments.
Concrete run-time exceptions that derived from ICEException provide a corresponding
init method and convenience constructor. For example, here is the Slice definition of
ObjectNotExistException:
local exception RequestFailedException {
Identity id;
string facet;
string operation;
};
local exception ObjectNotExistException
extends RequestFailedException {};
The Objective‑C mapping for
ObjectNotExistException is:
@interface ICEObjectNotExistException : ICERequestFailedException
// ...
‑(id) init:(const char*)file__p line:(int)line__p;
‑(id) init:(const char*)file__p
line:(int)line__p
id_:(ICEIdentity *)id_
facet:(NSString *)facet
operation:(NSString *)operation;
+(id) objectNotExistException:(const char*)file__p
line:(int)line__p;
+(id) objectNotExistException:(const char*)file__p
line:(int)line__p
id_:(ICEIdentity *)id_
facet:(NSString *)facet
operation:(NSString *)operation;
@end
In other words, as for user exceptions, run-time exceptions provide init methods and convenience constructors that accept arguments in base-to-derived order. This means that all run-time exceptions require a file name and line number when they are instantiated. For example, you can throw an
ICEObjectNotExistException as follows:
@throw [ICEObjectNotExistException
objectNotExistException:__FILE__ line:__LINE__];
If you throw this exception in the context of an executing operation on the server side, the
id_,
facet, and
operation instance variables are automatically initialized by the Ice run time.
When you instantiate a run-time exception, the base NSException is initialized such that its
name method returns the same string as
ice_name; the
reason and
userInfo methods return
nil.
User exceptions and run-time exceptions implement the NSCopying protocol, so you can copy them. The semantics are the same as for structures (see
page 523).
Similarly, like structures, exceptions implement a dealloc method that takes care of deallocating the instance variables when an exception is released.
Exceptions do not override isEqual or
hash, so these methods have the behavior inherited from
NSObject.