Using Exception Handling versus NSError in Cocoa Apps - objective-c

Hey all. I've been reading up on Apple's suggestions for when/where/how to use NSError versus #try/#catch/#finally. Essentially, my impression is that Apple thinks it best to avoid the use of exception handling language constructs except as a mechanism for halting program execution in unexpected error situations (maybe someone could give an example of such a situation?)
I come from Java, where exceptions are the way to go when one wants to handle errors. Admittedly, I'm still in the Java thoughtspace, but I'm slowly coming to grips with all that NSError has to offer.
One thing I'm hung up on is the task of cleaning up memory when an error occurs. In many situations (e.g. using C, C++ libraries, CoreFoundation, etc..) you have a lot of memory cleanup that needs to be done before breaking out of a function due to an error.
Here's an example I cooked up that accurately reflects the situations I've been encountering. Using some imaginary data structures, the function opens up a file handle and creates a 'MyFileRefInfo' object which contains information about what to do with the file. Some stuff is done with the file before the file handle is closed and the memory for the struct freed. Using Apple's suggestions I have this method:
- (BOOL)doSomeThingsWithFile:(NSURL *)filePath error:(NSError **)error
{
MyFileReference inFile; // Lets say this is a CF struct that opens a file reference
MyFileRefInfo *fileInfo = new MyFileRefInfo(...some init parameters...);
OSStatus err = OpenFileReference((CFURLRef)filePath ,&inFile);
if(err != NoErr)
{
*error = [NSError errorWithDomain:#"myDomain" code:99 userInfo:nil];
delete fileInfo;
return NO;
}
err = DoSomeStuffWithTheFileAndInfo(inFile,fileInfo);
if(err != NoErr)
{
*error = [NSError errorWithDomain:#"myDomain" code:100 userInfo:nil];
CloseFileHandle(inFile); // if we don't do this bad things happen
delete fileInfo;
return NO;
}
err = DoSomeOtherStuffWithTheFile(inFile,fileInfo);
if(err != NoErr)
{
*error = [NSError errorWithDomain:#"myDomain" code:101 userInfo:nil];
CloseFileHandle(inFile); // if we don't do this bad things happen
delete fileInfo;
return NO;
}
CloseFileHandle(inFile);
delete fileInfo;
return YES;
}
Now.. my Java logic tells me that it would be better to set this up as a try/catch/finally structure and put all the calls to close the file handle and free memory in the finally block.
Like so..
...
#try
{
OSStatus err = OpenFileReference((CFURLRef)filePath ,&inFile);
if(err != NoErr)
{
... throw some exception complete with error code and description ...
}
err = DoSomeStuffWithTheFileAndInfo(inFile,fileInfo);
if(err != NoErr)
{
... throw some exception ...
}
... etc ...
}
#catch(MyException *ex)
{
*error = [NSError errorWithDomain:#"myDomain" code:[ex errorCode] userInfo:nil];
return NO;
}
#finally
{
CloseFileHandle(inFile); // if we don't do this bad things happen
delete fileInfo;
}
return YES;
Am I crazy in thinking that this is a much more elegant solution with less redundant code?
Did I miss something?

Daniel's answer is correct, but this question deserves a rather more blunt answer.
Throw an exception only when a non-recoverable error is encountered.
Use NSError when communicating error conditions that may be recovered from.
Any exception that is thrown through a frame in Apple's frameworks may result in undefined behavior.
There is an Exceptions programming topic document available in the dev center.

Essentially, my impression is that Apple thinks it best to avoid the use of exception handling language constructs except as a mechanism for halting program execution in unexpected error situations (maybe someone could give an example of such a situation?)
That's not quite my impression. I thought that Apple suggests using exceptions for truly exceptional conditions, and NSError for expected failures. Since you come from Java, I think NSError -> java.lang.Exception, and Obj-C Exceptions -> java.lang.RuntimeException. Use an Obj-C exception when the programmer did something wrong (used an API incorrectly, for example), and use NSError when an expected failure occurred (the remote server could not be found, for example).
Of course, that's just my interpretation of Apple's position. I, on the other hand, like exceptions!

Exceptions in Objective-C have historically been 'heavy', with a performance cost to entering a try block, a cost to throwing, a cost to using finally, etc. As a result Cocoa developers have typically avoided exceptions outside of 'oh no, the sky is falling' sorts of situations -- if a file is missing, use an NSError, but if there's no filesystem and a negative amount of free memory, that's an exception.
That's the historical view. But if you're building a 64-bit app on 10.5 or newer, the exception architecture has been rewritten to be 'zero cost', which may mean that the historical view is no longer relevant. As with just about anything, it comes down to various factors -- if working one way is more natural to you and will let you finish more quickly, and if you don't experience any performance-related problems with it, and if being slightly inconsistent with 'traditional' Objective-C code doesn't bother you... then there's no reason not to use exceptions.

According to More iPhone 3 Development by Dave Mark and Jeff LeMarche, exceptions in are used only for truly exceptional situations and usually indicate a problem within your code. You should never use exceptions to report a run-of-the-mill error condition. Exceptions are used with much less frequency in Objective-C than in many other languages, such as Java and C++.
You use an exception when you need to catch a mistake in your code. You use an error when the user may need to fix the problem.
Here's an example where you would use an exception:
We're writing a superclass, and we want to make sure its subclasses implement a given method. Objective-C doesn't have abstract classes, and it lacks a mechanism to force a subclass to implement a given method. Yet we can use an exception to instantly inform us that we forgot to implement the method in a subclass. Instead of an unpredictable behavior, we'll get slammed with a runtime exception. We can easily debug it because our exception will tell us exactly what we did wrong:
NSException *ex = [NSException exceptionWithName:#"Abstract Method Not Overridden" reason:NSLocalizedString(#"You MUST override the save method", #"You MUST override the save method") userInfo:nil];
[ex raise];
Because problem is a programmer mistake rather than a problem the user may be able to fix, we use an exception.

Related

What to do if [super init] returns nil?

Take the following code as an example
- (id)init {
self = [super init];
if (self) {
// code
}
return self;
}
I do not want nil to propagate up the calling hierarchy. My initial idea is to throw an exception in case self is nil, make a restore point and abort execution.
Better ideas?
NSObject's implementation of [super init] will never return nil. The base implementation just returns self.
In general, the only reason that an initializer returns nil is if a nonfatal error occurred. For example, you might have called -initWithContentsOfURL:error: and passed an invalid URL. By convention, methods that may fail in this way have an error: parameter, which contains information about the failure. Most initializers do not have the possibility of a recoverable error, so like NSObject, they will never return nil.
Fatal errors typically throw an exception or abort the program. So checking for nil is no help with them. Your best bet to handle fatal errors is NSSetUncaughtExceptionHandler, although you should be aware that saving data is risky in the case of a fatal error, as the unsaved data may be corrupted. Don't overwrite good data in that case.
Why does objective-c code always check for nil in initializers, even when super will never return nil? Convention, mostly. Arguably, by always checking for nil, it becomes easier for a superclass to add a failure condition in the future without requiring subclasses to be changed, but really it's just a convention.
Finally, the initalizer is not the right place to check for failure in a superclass initializer. If recoverable errors are a possibility, the caller should check for the error.
Example:
NSError *error;
FooClass *myFoo = [[FooClass alloc] initWithContentsOfURL:blah error:&error]
if (myFoo == nil) {
// ...
} else {
// ...
}
Checking for nil whenever you initialize an object is overkill. This only needs to be done when there is an error: argument, or the method has a documented recoverable error.
From the docs:-
For other sorts of errors, including expected runtime errors, return
nil, NO, NULL, or some other type-‐suitable form of zero to the
caller. Examples of these errors include the inability to read or
write a file, a failure to initialize an object, the inability to
establish a network connection, or a failure to locate an object in a
collection. Use an NSError object if you feel it necessary to return
supplemental information about the error to the sender. An NSError
object encapsulates information about an error, including an error
code (which can be specific to the Mach, POSIX, or OSStatus domains)
and a dictionary of program-‐specific information. The negative value
that is directly returned (nil, NO, and so on) should be the principal
indicator of error; if you do communicate more specific error
information, return an NSError object indirectly in a parameter of the
method.
Generally speaking, just don't care and let the nil propagate.
If [super init] is returning nil (i.e. a new object could not be instantiated), something is messed up so badly that your application is likely going to crash in a matter of moments anyway.
Checking for nil after every instantiation as suggested by Michael is cumbersome and probably totally useless for the reasons above.
If there's a specific class that you are worried about, and you really want to bail out as quick as possible, go ahead as you planned and throw an exception.
About "making a restore point" you can try to save whatever possible, but the scenario is so compromised that there's no guarantee of succeeding.
I suppose "[super init]" could throw nil if you're trying to reserve a 20 gig block of memory or something (which nobody doing iOS coding would ever do), but in general, a "nil" being returned rarely happens in production code.
The nice thing about "nil" being returned is that you can send messages to a nil object and your application won't crash.
But you should always do checks for nil after instantiating an object, just to make sure you don't get too deep into something your app (or your user) can't recover from.
In Apple's "Concepts in Objective C" document, they suggest "When you create an object, you should generally check whether the returned value is nil before proceeding:".

NSKeyedUnarchiver - try/catch needed?

As I understand, the use of #try/#catch blocks is discouraged, because exceptions should only be thrown at unrecoverable, catastrophic errors (refer to this discussion with a nice answer by #bbum: Exception Handeling in iOS).
So I looked through my code and found a #try/#catch block that I don't know how to get rid of:
NSData *fileData = [NSData dataWithContentsOfFile: ....];
NSDictionary *dictionary;
#try {
dictionary = [NSKeyedUnarchiver unarchiveObjectWithData: fileData];
}
#catch (NSException *exception) {
//....
}
#finally {
//...
}
The problem is that (as stated in the documentation) +unarchiveObjectWithData: raises an NSInvalidArchiveOperationException if the NSData doesn't contain a valid archive.
Since the data is provided by a file the user chose, it is not guaranteed that it contains a valid archive, and thus the application would crash if a incorrect file was chosen.
Now two questions:
Why doesnt +unarchiveObjectWithData: just return nil (Edit: and an NSError**) if the archive is not valid (this doesn't seem to qualify as a catastrophic or unrecoverable error).
Is the pattern above correct (using #try)? I have found no method that lets us check if the data contains a valid archive beforehand and have found no possibility to handle this case using the delegate protocol. Antyhing I overlooked?
Note that the code above of course works, I just wonder if its the best practice.
There was a new method added in iOS 9 to NSKeyedUnarchiver that now returns an error:
Swift:
public class func unarchiveTopLevelObjectWithData(data: NSData) throws -> AnyObject?
Objective-C:
+ (nullable id)unarchiveTopLevelObjectWithData:(NSData *)data error:(NSError **)error;
However, this is not backwards compatible with previous versions of iOS, so you will need to check for framework availability.
NSKeyedArchiver is built by Apple. They control the code that is performed while unarchiveObjectWithData: executes so they also control resource management during exception handling (which is the source of trouble behind exceptions in Objective-C).
If they can guarantee that in between your call to unarchiveObjectWithData: and the point in code where they raise the exception is no foreign code (neither third party nor your app's code) it is in theory possible to safely use an exception, as long as calling code takes care of cleaning up correctly.
The problem is that this assumption might not be the case: It is common to use NSKeyedArchiver to serialize custom objects. Usually the custom class implements initWithCoder: to read the classes data (by using the archiver's methods like decodeObjectForKey:).
If the archiver throws an exception in one of these methods there's no way to fix resource handling for the archiver. The exception will be thrown through the custom object's initWithCoder:. The archiver does not know if there's more stuff to clean up than the deserialized objects. So in this scenario the occurrence of the exception means that the process is in a dangerous state and unwanted behavior may result.
Regarding your questions:
Why doesn't [NSKeyedArchiver use proper Cocoa error handling]?
Only the Apple engineers who built the archiver know. My guess is that exception handling and keyed archiving were built at roughly the same time (around 2001) and at that point it wasn't yet clear that exception handling would never be a first class citizen in Objective-C.
Is the #try pattern correct?
With the limitation of the caveats described above it is correct. If Apple's code handles the exception cases properly and your own serialization code does the same the #try pattern might be correct.
It is very difficult to achieve full correctness, though. You'd have to make sure all executed code is aware of the exceptions and does cleanup correctly.
ARC, for instance, does no exception cleanup for local variables and temporary objects by default (you would have to enable -fobjc-arc-exceptions to do this).
Also, there's no documentation on exception safety of the accessors of #synthesized properties (when atomic they might leak a lock).
Conclusion:
There are a myriad of subtle ways of how exceptions can break stuff. It is difficult and requires in depth knowledge of the implementation of all involved parts to build exception safe code in Objective-C.
All this leads to the conclusion. If you want to handle errors gracefully while loading possibly corrupted archives and continue normal execution afterwards: Do not use NSKeyedArchiver.

Objective-C should I be using NSError and why presentError function not working

I am creating an application on iPhone where I am taking code written and Java and translating into Objective-C, as the app must work similarly in both Android and iPhone. Now problems of course are there are certain areas where the languages differ quite a bit, and can be difficult to implement just right.
One of the areas where it is of course different is you cannot create NSMutableArrays or NSMutableDictionaries which only accepts objects of a certain type. So when I am creating the getters and setters for these objects, I have been doing this, example below:
-(void)setPerson:(NSMutableArray *)personList_p
{
BOOL bAllowed = YES;
for(int i = 0; i < [personList_p count]; i++)
{
if(![[personList_p objectAtIndex:i] isKindOfClass:[Person class]])
{
bAllowed = NO;
break;
}
else
{
bAllowed = YES;
}
}
if(bAllowed)
{
personList_i = personList_p;
}
else
{
//Raise error here
}
}
Now originally I was using NSRaise Exception, but after doing reading up on it, it recommends against this as exception handling is resource intensive and should not be used for this type of situation. So NSError I read was a good solution, so I was reading up on this guide
http://www.cimgf.com/2008/04/04/cocoa-tutorial-using-nserror-to-great-effect/
My question is two fold, if I decide to use NSError should, is it best practice to declare the NSError object in the method, i.e like this
-(void)setPerson:(NSMutableArray *)personList_p : (NSError **)error
Or is it okay just to declare the error within the else statement above, like this
NSError *error = nil;
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:#"Invalid object passed into Array, object must be of type Person." forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"Invalid Object" code:200 userInfo:details];
NSLog(#"%#", [error localizedDescription]);
Secondly, in the guide I linked above, in the conclusion the author says
In this example, I am checking to see if the error is still nil after
my message call. If it is no longer nil I know that an error occurred
and that I need to display it to the user. Apple provides a built in
method to do this with a call to presentError: on the NSApplication
instance
and he uses this line of code here
[NSApp presentError:error];
This does not work for me, I do not get an autocomplete option with this code, just says use of undeclared identifier. Am I missing something obvious here?
Also, just to be sure, is the method I am using, the best method to go about ensuring the user sends in an array with the correct object types? Or is there another method which could be used which would achieve the same thing but in a more efficient manner.
Thanks in advance!!
EDIT:
Additional question, would it be such a bad idea to use NSException, as the problem with NSError is the app carries on, I am considering having it so if they do pass in an array with an invalid object, that it does just cause the application to crash and raise an exception. Is there any major reason why I shouldn't do this??
Now originally I was using NSRaise Exception, but after doing reading
up on it, it recommends against this as exception handling is resource
intensive and should not be used for this type of situation. So
NSError I read was a good solution, so I was reading up on this guide
He does not actually say that raising exceptions is inherently bad or resource intensive, he argues that #try/catch is the wrong way to go about things. That, and Cocoa applications (even more so I'm Cocoa-Touch) should refrain from throwing exceptions whenever possible. Exceptions indicate undefined behavior, not simple errors.
Secondly, in the guide I linked above, in the conclusion the author...
uses this line of code here
[NSApp presentError:error];
This does not work for me, I do not get an autocomplete option with
this code, just says use of undeclared identifier. Am I missing
something obvious here?
That is because NSApp is a concept only accessible to Cocoa(or Mac) applications. It represents a pointer to "the application itself", and has some pretty neat functions associated with it. Of course, iOS has no parallel concept, so errors are traditionally presented in a UIAlertView.
Additional question, would it be such a bad idea to use NSException,
as the problem with NSError is the app carries on, I am considering
having it so if they do pass in an array with an invalid object, that
it does just cause the application to crash and raise an exception. Is
there any major reason why I shouldn't do this??
It is generally a bad idea to use NSException for anything other than extremely serious logic (such as when a call is made to an index out of the bounds of an NSArray, or when a UITableView's data source happens to be out of sync with what the table demands). If you absolutely must stop program execution, it's much cleaner to use NSAssert() to throw conditional exceptions. And even then, setters are not the place to be throwing exceptions. It would be far easier to simply return nil when the condition fails, then have the caller check for such an outcome and handle it appropriately.

In a #try-#catch-#finally block, is it good use finally or to continue normally?

This is a simple Objective-C question.
When you use a #trythe work flow can run in 2 ways
If some NSException appear, the code immediately jump to #catch block and than to #finally
If not, finish to run the #try block and than run #finally
So, what is the difference to use or not use the #finally block? If I use only:
-(void)function{
#try {
...
}
#catch (NSException *exception) {
...
}
>>>The workflow will run this line in any case?
}
Than the rest of the function will run, or only the #catch block if a NSException is created?
"A #finally block contains code that must be executed whether an exception is thrown or not."
Does code in finally get run after a return in Objective-C?
The finally block exists to release/clean up resources like open sockets, open files, database locks, semaphore locks, and so on.
If an error occurs inside of the catch block or the catch block rethrows the exception, then the line:
>>>The workflow will run this line in any case?
is not executed. However, the code in the finally block should be executed. The finally block is the last, best chance to exit cleanly from an application that is about to crash. Even if the application is not about to crash, it is still the best place to cleanup resources because the code inside the finally block is more likely to be executed under unexpected conditions than code outside of the finally block.
A couple things to note:
The #catch block is not required, you can have #try-#finally, and
use the #finally block for anything (e.g. cleanup) that must happen
even if an exception occurs
The #catch block doesn't have to catch
NSException, it may (and probably should) be changed to catch more specific
exceptions. In that case the #catch block, along with the code below
the #try-#catch-#finally, would not be run depending on the exception
Few important points that were missed in other's answers here.
Apple does NOT recommend the use of #try #catch #finally clauses in production code. Their penalty is too high, and the real benefits are few. In C++ where defensive code is all over the place, you can design your application to be "exception based" meaning, all the code is designed for stack-rollback and throwing and rethrowing exceptions until they reach the top level of the stack. This is NOT the case in Obj-C, and even in C++ this paradigm kills most compiler optimizations, because the compiler cannot shortcut any scenario as an exception can break it in the middle.
However --- Apple provides the try/catch/finally mechanism in Obj-C, so you can create debug-configuration-only code, that will help you identify and catch (literally) your bugs BEFORE releasing the application to the public.
In addition Apple provides a complete (and beautiful) "Error handling" paradigm and protocol, backed up in API (NSError object, nested NSErrors NSError recovery protocol, NSError display API etc.) that is suitable for runtime error handling in your application - in "release" builds of your applications.
The above is correct for both iOS and MacOS-X error handling.
So, the whole discussion about the use of #finally here are a little exaggerated.

Is there a way to trap messages sent to nil in Objective-C?

I've just been bitten by an annoying bug that was made obscure by the "send message to nil is ok" behaviour in Objective-C.
I've seen Sending a message to nil?, and the consensus seems to be 'thats how we roll' in Objective-C.
Now, maybe I don't have enough experience in Objective-C, but it seems like it would be useful to trap this, because I can't think of a good reason why this should be happening most of the time. However, It could just be a coding idiom I'm not used to yet.
So other than checking for nil everywhere like so:
assert( object != nil );
[object message];
Is there a way to make the runtime trap this condition, and warn when object is nil?
You could use a ObjC undocumented trick or dtrace (see the comments for the dtrace solution).
pid$1::objc_msgSend:entry
/arg0==0/
{
ustack();
}
nil messaging is used very commonly in ObjC. Folks can fight over whether this is good or bad; it's just something you have to get used to. If you try to break it with tricks, then you're going to break Cocoa because Cocoa uses it. There are some tricks (like diciu posted) that will make it possible to debug in situations where you suspect nil messaging but just can't seem to find it. But you can't just leave those in your code (and the blog post above makes that clear). nil messaging is just too common inside the frameworks.
To your original point, though, compare:
- (void)doSomethingWith:(id)x {
NSAssert(x != nil, #"Don't pass me nil");
[x something];
}
vs.
void Bar::DoSomething(Foo *x) {
assert(x != NULL);
if (x != NULL) {
x.something;
}
}
In both cases, you need to test, and in both cases the compiler won't warn you if you fail to test. The only difference is in what cases you crash/assert. Personally, I write macros around NSAssert() that make it always print a log message if it fails. It just only crashes in Debug. That way when a customer sends me logs, I can see what assertions failed.