1@paragraphindent 0
2
3@node Exception Handling
4@chapter Exception Handling, Logging, and Assertions
5@cindex exception facilities
6@cindex logging facilities
7@cindex assertion facilities
8
9No matter how well a program is designed, if it has to interact with a user or
10other aspect of the outside world in any way, the code is bound to
11occasionally meet with cases that are either invalid or just plain unexpected.
12A very simple example is when a program asks the user to enter a filename, and
13the user enters the name of a file that does not exist, or does not enter a
14name at all.  Perhaps a valid filename @i{is} entered, but, due to a previous
15disk write error the contents are garbled.  Any number of things can go wrong.
16In addition, programmer error inevitably occurs and needs to be taken account
17of.  Internal functions may be called with invalid arguments, either due to
18unexpected paths being taken through the code, or silly things like typos
19using the wrong variable for something.  When these problems happen (and they
20@i{will} happen), it is better to handle them gracefully than for the program
21to crash, or worse, to continue processing but in an erroneous way.
22
23To allow for this, many computer languages provide two types of facilities.
24The first is referred to as @i{exception handling} or sometimes @i{error
25trapping}.  The second is referred to as @i{assertion checking}.  Exceptions
26allow the program to catch errors when they occur and react to them
27explicitly.  Assertions allow a programmer to establish that certain
28conditions hold before attempting to execute a particular operation.  GNUstep
29provides both of these facilities, and we will cover each in turn.  The
30assertion facility is tied in with the GNUstep @i{logging} facilities, so we
31describe those as well.
32
33To use any of the facilities described in this chapter requires that you
34include @code{Foundation/NSException.h}.
35
36
37@section Exceptions
38@cindex exceptions
39@cindex NSException class
40@cindex NS_DURING macro
41@cindex NS_HANDLER macro
42@cindex NS_ENDHANDLER macro
43@cindex NSUncaughtExceptionHandler
44
45GNUstep exception handling provides for two things:
46
47@enumerate
48@item
49When an error condition is detected during execution, control is passed to a
50special error-handling routine, which is given information on the error that
51occurred.
52@item
53This routine may itself, if it chooses, pass this information up the function
54call stack to the next higher level of control.  Often higher level code is
55more aware of the context in which the error is occurring, and can therefore
56make a better decision as to how to react.
57@end enumerate
58
59
60@subsection Catching and Handling Exceptions
61
62GNUstep exception handling is implemented through the macros @code{NS_DURING},
63@code{NS_HANDLER}, and @code{NS_ENDHANDLER} in conjunction with the
64@code{NSException} class.  The following illustrates the pattern:
65
66@example
67NS_DURING
68  @{
69    // do something risky ...
70  @}
71NS_HANDLER
72  @{
73    // a problem occurred; inform user or take another tack ...
74  @}
75NS_ENDHANDLER
76  // back to normal code...
77@end example
78
79For instance:
80
81@example
82- (DataTree *) readDataFile: (String *)filename
83@{
84  ParseTree *parse = nil;
85  NS_DURING
86    @{
87      FileHandle *handle = [self getFileHandle: filename];
88      parse = [parser parseFile: handle];
89      if (parse == nil)
90        @{
91          NS_VALUERETURN(nil);
92        @}
93    @}
94  NS_HANDLER
95    @{
96      if ([[localException name] isEqualToString: MyFileNotFoundException])
97        @{
98          return [self readDataFile: fallbackFilename];
99        @}
100      else if ([[localException name] isEqualToString: NSParseErrorException])
101        @{
102          return [self readDataFileInOldFormat: filename];
103        @}
104      else
105        @{
106          [localException raise];
107        @}
108    @}
109  NS_ENDHANDLER
110  return [[DataTree alloc] initFromParseTree: parse];
111@}
112@end example
113
114Here, a file is parsed, with the possibility of at least two different errors:
115not finding the file and the file being misformatted.  If a problem does
116occur, the code in the @code{NS_HANDLER} block is jumped to.  Information on
117the error is passed to this code in the @code{localException} variable, which
118is an instance of @code{NSException}.  The handler code examines the name of
119the exception to determine if it can implement a work-around.  In the first
120two cases, an alternative approach is available, and so an alternative value
121is returned.
122
123If the file is found but the parse simply produces a nil parse tree, the
124@code{NS_VALUERETURN} macro is used to return nil to the
125@code{readDataFile:} caller.  Note that it is @i{not} allowed to simply write
126``@code{return nil;}'' inside the NS_DURING block, owing to the nature of the
127behind-the-scenes C constructs implementing the mechanism (the @code{setjmp()}
128and @code{longjmp()} functions).  If you are in a void function not returning
129a value, you may use simply ``@code{NS_VOIDRETURN}'' instead.
130
131Finally, notice
132that in the third case above the handler does not recognize the exception
133type, so it passes it one level up to the caller by calling @code{-raise} on
134the exception object.
135
136
137@subsection Passing Exceptions Up the Call Stack
138
139If the caller of @code{-readDataFile:} has enclosed the call inside its own
140@code{NS_DURING} @dots{} @code{NS_HANDLER} @dots{} @code{NS_ENDHANDLER} block,
141it will be able to catch this exception and react to it in the same way as we
142saw here.  Being at a higher level of execution, it may be able to take
143actions more appropriate than the @code{-readDataFile:} method could have.
144
145If, on the other hand, the caller had @i{not} enclosed the call, it would not
146get a chance to react, but the exception would be passed up to the caller of
147@i{this} code.  This is repeated until the top control level is reached, and
148then as a last resort @code{NSUncaughtExceptionHandler} is called.  This is a
149built-in function that will print an error message to the console and exit
150the program immediately.  If you don't want this to happen it is possible to
151override this function by calling
152@code{NSSetUncaughtExceptionHandler(fn_ptr)}.  Here, @code{fn_ptr} should be
153the name of a function with this signature (defined in @code{NSException.h}):
154
155@example
156void NSUncaughtExceptionHandler(NSException *exception);
157@end example
158
159One possibility would be to use this to save files or any other unsaved state
160before an application exits because of an unexpected error.
161
162
163@subsection Where do Exceptions Originate?
164
165You may be wondering at this point where exceptions come from in the first
166place.  There are two main possibilities.  The first is from the Base library;
167many of its classes raise exceptions when they run into error conditions.  The
168second is that application code itself raises them, as described in the next
169section.  Exceptions do @i{not} arise automatically from C-style error
170conditions generated by C libraries.  Thus, if you for example call the
171@code{strtod()} function to convert a C string to a double value, you still
172need to check @code{errno} yourself in standard C fashion.
173
174Another case that exceptions are @i{not} raised in is in the course of
175messaging.  If a message is sent to @code{nil}, it is silently ignored
176without error.  If a message is sent to an object that does not implement it,
177the @code{forwardInvocation} method is called instead, as discussed in
178@ref{Advanced Messaging}.
179
180
181@subsection Creating Exceptions
182
183If you want to explicitly create an exception for passing a particular error
184condition upwards to calling code, you may simply create an
185@code{NSException} object and @code{raise} it:
186
187@example
188NSException myException = [[NSException alloc]
189                              initWithName: @@"My Exception"
190                                    reason: @@"[Description of the cause...]"
191                                  userInfo: nil];
192[myException raise];
193 // code in block after here is unreachable..
194@end example
195
196The @code{userInfo} argument here is a @code{NSDictionary} of key-value pairs
197containing application-specific additional information about the error.  You
198may use this to pass arbitrary arguments within your application.  (Because
199this is a convenience for developers, it should have been called
200@code{developerInfo}..)
201
202Alternatively, you can create the exception and raise it in one call with
203@code{+raise}:
204
205@example
206[NSException raise: @@"My Exception"
207            format: @@"Parse error occurred at line %d.",lineNumber];
208@end example
209
210Here, the @code{format} argument takes a printf-like format analogous to
211@code{[NSString -stringWithFormat:]} discussed @ref{Objective-C, previously,
212Strings in GNUstep}.  In general, you should not use arbitrary names for
213exceptions as shown here but constants that will be recognized throughout your
214application.  In fact, GNUstep defines some standard constants for this
215purpose in @code{NSException.h}:
216
217@table @code
218@item NSCharacterConversionException
219An exception when character set conversion fails.
220@item NSGenericException
221A generic exception for general purpose usage.
222@item NSInternalInconsistencyException
223An exception for cases where unexpected state is detected within an object.
224@item NSInvalidArgumentException
225An exception used when an invalid argument is passed to a method or function.
226@item NSMallocException
227An exception used when the system fails to allocate required memory.
228@item NSParseErrorException
229An exception used when some form of parsing fails.
230@item NSRangeException
231An exception used when an out-of-range value is encountered.
232@end table
233
234Also, some Foundation classes define their own more specialized exceptions:
235
236@table @code
237@item NSFileHandleOperationException (NSFileHandle.h)
238An exception used when a file error occurs.
239@item NSInvalidArchiveOperationException (NSKeyedArchiver.h)
240An archiving error has occurred.
241@item NSInvalidUnarchiveOperationException (NSKeyedUnarchiver.h)
242An unarchiving error has occurred.
243@item NSPortTimeoutException (NSPort.h)
244Exception raised if a timeout occurs during a port send or receive operation.
245@item NSUnknownKeyException (NSKeyValueCoding.h)
246 An exception for an unknown key.
247@end table
248
249
250@subsection When to Use Exceptions
251
252As might be evident from the @code{-readDataFile:} example above, if a
253certain exception can be anticipated, it can also be checked for, so you
254don't necessarily need the exception mechanism.  You may want to use
255exceptions anyway if it simplifies the code paths.  It is also good practice
256to catch exceptions when it can be seen that an unexpected problem might
257arise, as any time file, network, or database operations are undertaken, for
258instance.
259
260Another important case where exceptions are useful is when you need to pass
261detailed information up to the calling method so that it can react
262appropriately.  Without the ability to raise an exception, you are limited to
263the standard C mechanism of returning a value that will hopefully be
264recognized as invalid, and perhaps using an @code{errno}-like strategy where
265the caller knows to examine the value of a certain global variable.  This is
266inelegant, difficult to enforce, and leads to the need, with void methods, to
267document that ``the caller should check @code{errno} to see if any problems
268arose''.
269
270
271@section Logging
272@cindex logging
273@cindex NSLog function
274@cindex NSDebugLog function
275@cindex NSWarnLog function
276@cindex profiling facilities
277
278GNUstep provides several distinct logging facilities best suited for different
279purposes.
280
281@subsection NSLog
282
283The simplest of these is the @code{NSLog(NSString *format, ...)}  function.
284For example:
285
286@example
287NSLog(@@"Error occurred reading file at line %d.", lineNumber);
288@end example
289
290This would produce, on the console (stderr) of the application calling it,
291something like:
292
293@example
2942004-05-08 22:46:14.294 SomeApp[15495] Error occurred reading file at line 20.
295@end example
296
297The behavior of this function may be controlled in two ways.  First, the user
298default @code{GSLogSyslog} can be set to ``@code{YES}'', which will send
299these messages to the syslog on systems that support that (Unix variants).
300Second, the function GNUstep uses to write the log messages can be
301overridden, or the file descriptor the existing function writes to can be
302overridden:
303@comment{Need ref to where user defaults are explained.}
304
305@example
306  // these changes must be enclosed within a lock for thread safety
307NSLock *logLock = GSLogLock();
308[logLock lock];
309
310  // to change the file descriptor:
311_NSLogDescriptor = <fileDescriptor>;
312  // to change the function itself:
313_NSLog_printf_handler = <functionName>;
314
315[logLock unlock];
316@end example
317
318Due to locking mechanisms used by the logging facility, you should protect
319these changes using the lock provided by @code{GSLogLock()} (see @ref{Base
320Library, , Threads and Run Control} on locking).
321
322The @code{NSLog} function was defined in OpenStep and is also available in Mac
323OS X Cocoa, although the overrides described above may not be.  The next set of
324logging facilities to be described are only available under GNUstep.
325
326
327@subsection NSDebugLog, NSWarnLog
328
329The facilities provided by the @code{NSDebugLog} and @code{NSWarnLog} families
330of functions support source code method name and line-number reporting and
331allow compile- and run-time control over logging level.
332
333The @code{NSDebugLog} functions are enabled at compile time by default.  To
334turn them off, set @code{'diagnose = no'} in your makefile, or undefine
335@code{GSDIAGNOSE} in your code before including @code{NSDebug.h}.  To turn
336them off at runtime, call @code{[[NSProcessInfo processInfo]
337setDebugLoggingEnabled: NO]}.  (An @code{NSProcessInfo} instance is
338automatically instantiated in a running GNUstep application and may be
339obtained by invoking @code{[NSProcessInfo processInfo]}.)
340
341At runtime, whether or not logging is enabled, a debug log method is called
342like this:
343
344@example
345NSDebugLLog(@@"ParseError", @@"Error parsing file at line %d.", lineNumber);
346@end example
347
348Here, the first argument to @code{NSDebugLog}, ``@code{ParseError}'', is a
349string @i{key} that specifies the category of message.  The message will only
350actually be logged (through a call to @code{NSLog()}) if this key is in the
351set of active debug categories maintained by the @code{NSProcessInfo} object
352for the application.  Normally, this list is empty.  There are
353three ways for string keys to make it onto this list:
354
355@itemize
356@item
357Provide one or more startup arguments of the form @code{--GNU-Debug=<key>} to
358the program.  These are processed by GNUstep and removed from the argument
359list before any user code sees them.
360@item
361Call @code{[NSProcessInfo debugSet]} at runtime, which returns an
362@code{NSMutableSet}.  You can add (or remove) strings to this set directly.
363@item
364The @code{GNU-Debug} user default nay contain a comma-separated list of keys.
365However, note that @code{[NSUserDefaults standardUserDefaults]} must first be
366called before this will take effect (to read in the defaults initially).
367@end itemize
368
369While any string can be used as a debug key, conventionally three types of
370keys are commonly used.  The first type expresses a ``level of importance''
371for the message, for example, ``Debug'', ``Info'', ``Warn'', or ``Error''.
372The second type of key that is used is class name.  The GNUstep Base classes
373used this approach.  For example if you want to activate debug messages for
374the @code{NSBundle}'' class, simply add '@code{NSBundle}' to the list of keys.
375The third category of key is the default key, '@code{dflt}'.  This key can be
376used whenever the specificity of the other key types is not required.  Note
377that it still needs to be turned on like any other logging key before
378messasges will actually be logged.
379
380There is a family of @code{NSDebugLog} functions with slightly differing
381behaviors:
382
383@table @code
384@item NSDebugLLog(key, format, args,...)
385Basic debug log function already discussed.
386@item NSDebugLog(format, args,...)
387Equivalent to @code{NSDebugLLog} with key ``dflt'' (for default).
388@item NSDebugMLLog(level, format, args,...)
389Equivalent to @code{NSDebugLLog} but includes information on which method the
390logging call was made from in the message.
391@item NSDebugMLog(format, args,...)
392Same, but use 'dflt' log key.
393@item NSDebugFLLog(level, format, args,...)
394As @code{NSDebugMLLog} but includes information on a function rather than a
395method.
396@item NSDebugFLog(format, args,...)
397As previous but using 'dflt' log key.
398@end table
399
400The implementations of the @code{NSDebugLog} functions are optimized so that
401they consume little time when logging is turned off.  In particular, if debug
402logging is deactivated at compile time, there is NO performance cost, and if
403it is completely deactivated at runtime, each call entails only a boolean
404test.  Thus, they can be left in production code.
405
406There is also a family of @code{NSWarn} functions.  They are similar to the
407@code{NSDebug} functions except that they do not take a key.  Instead, warning
408messages are shown by default unless they are disabled at compile time by
409setting @code{'warn = no'} or undefining @code{GSWARN}, or at runtime by
410@i{adding} ``@code{NoWarn}'' to @code{[NSProcessInfo debugSet]}.
411(Command-line argument @code{--GNU-Debug=NoWarn} and adding ``NoWarn'' to the
412@code{GNU-Debug} user default will also work.)  @code{NSWarnLog()},
413@code{NSWarnLLog()}, @code{NSWarnMLLog}, @code{NSWarnMLog},
414@code{NSWarnFLLog}, and @code{NSWarnFLog} are all similar to their
415@code{NSDebugLog} counterparts.
416
417
418@subsection Last Resorts: GSPrintf and fprintf
419
420Both the @code{NSDebugLog} and the simpler @code{NSLog} facilities utilize a
421fair amount of machinery - they provide locking and timestamping for example.
422Sometimes this is not appropriate, or might be too heavyweight in a case where
423you are logging an error which might involve the application being in some
424semi-undefined state with corrupted memory or worse.  You can use the
425@code{GSPrintf()} function, which simply converts a format string to UTF-8 and
426writes it to a given file:
427
428@example
429GSPrintf(stderr, "Error at line %d.", n);
430@end example
431
432If even this might be too much (it uses the @code{NSString} and @code{NSData}
433classes), you can always use the C function @code{fprintf()}:
434
435@example
436fprintf(stderr, "Error at line %d.", n);
437@end example
438
439Except under extreme circumstances, the preferred logging approach is either
440@code{NSDebugLog}/@code{NSWarnLog}, due the the compile- and run-time
441configurability they offer, or @code{NSLog}.
442
443
444@subsection Profiling Facilities
445
446GNUstep supports optional programmatic access to object allocation
447statistics.  To initiate collection of statistics, call the function
448@code{GSDebugAllocationActive(BOOL active)} with an argument of
449``@code{YES}''.  To turn it off, call it with ``@code{NO}''.  The overhead
450of statistics collection is only incurred when it is active.  To access the
451statistics, use the set of @code{GSDebugAllocation...()} functions defined in
452@code{NSDebug.h}.
453
454
455@section Assertions
456@cindex assertions
457@cindex NSAssert macro
458@cindex NSAssertionHandler class
459
460Assertions provide a way for the developer to state that certain conditions
461must hold at a certain point in source code execution.  If the conditions do
462not hold, an exception is automatically raised (and succeeding code in the
463block is not executed).  This avoids an operation from taking place with
464illegal inputs that may lead to worse problems later.
465
466The use of assertions is generally accepted to be an efficient means of
467improving code quality, for, like unit testing, they can help rapidly uncover
468a developer's implicit or mistaken assumptions about program behavior.
469However this is only true to the extent that you carefully design the nature
470and placement of your assertions.  There is an excellent discussion of this
471issue bundled in the documentation with Sun's Java distribution.
472@comment{Add link to appropriate java.sun.com page.}
473
474@subsection Assertions and their Handling
475
476Assertions allow the developer to establish that certain conditions hold
477before undertaking an operation.  In GNUstep, the standard means to make an
478assertion is to use the @code{NSAssert} or @code{NSCAssert} macros.  The
479general form of these macros is:
480
481@example
482NSAssert(<boolean test>, <formatString>, <argumentsToFormat>);
483@end example
484
485For instance:
486
487@example
488NSAssert(x == 10, "X should have been 10, but it was %d.", x);
489@end example
490
491If the test '@code{x == 10}' evaluates to @code{true}, @code{NSLog()} is
492called with information on the method and line number of the failure, together
493with the format string and argument.  The resulting console message will look
494like this:
495
496@example
497Foo.m:126  Assertion failed in Foo(instance), method Bar.  X should have been
49810, but it was 5.
499@end example
500
501After this is logged, an exception is raised of type
502'@code{NSInternalInconsistencyException}', with this string as its
503description.
504
505If you need to make an assertion inside a regular C function (not an
506Objective-C method), use the equivalent macro @code{NSCAssert()}, etc..
507
508@i{@b{Note}}, you can completely disable assertions (saving the time for the
509boolean test and avoiding the exception if fails) by putting @code{#define
510NS_BLOCK_ASSERTIONS} before you include @code{NSException.h}.
511
512
513@subsection Custom Assertion Handling
514
515The aforementioned behavior of logging an assertion failure and raising an
516exception can be overridden if desired.  You need to create a subclass of
517@code{NSAssertionHandler} and register an instance in each thread in which
518you wish the handler to be used.  This is done by calling:
519
520@example
521[[[NSThread currentThread] threadDictionary]
522    setObject:myAssertionHandlerInstance forKey:@"NSAssertionHandler"];
523@end example
524
525See @ref{Base Library, , Threads and Run Control} for more information on what
526this is doing.
527
528
529@section Comparison with Java
530@cindex exception handling, compared with Java
531@cindex logging, compared with Java
532@cindex assertion handling, compared with Java
533
534GNUstep's exception handling facilities are, modulo syntax, equivalent to
535those in Java in all but three respects:
536
537@itemize
538@item
539There is no provision for a ``finally'' block executed after either the main
540code or the exception handler code.
541@item
542You cannot declare the exception types that could be raised by a method in its
543signature.  In Java this is possible and the compiler uses this to enforce
544that a caller should catch exceptions if they might be generated by a method.
545@item
546Correspondingly, there is no support in the @ref{GSDoc, documentation system}
547for documenting exceptions potentially raised by a method.  (This will
548hopefully be rectified soon.)
549@end itemize
550
551The logging facilities provided by @code{NSDebugLog} and company are similar
552to but a bit more flexible than those provided in the Java/JDK 1.4 logging APIs,
553which were based on the IBM/Apache Log4J project.
554
555The assertion facilities are similar to but a bit more flexible than those in
556Java/JDK 1.4 since you can override the assertion handler.
557
558@page
559