In Objective-C, there are several methods like initWithContentsOfFile:encoding:error:
where one passes in a reference to an NSError
object for the error:
parameter. In this example, the value of the NSError
object passed in can change based on what goes on at runtime when the method is being called and whether the body of the method was executed in a certain way successfully. In a way I think of this NSError
object as sort of like a second return value from the method, and only differs from an object anObject
in the statement return anObject;
in that when this statement is called, execution leaves the method.
So my question is, not only in the context of error handling in Objective-C, but in general, when should one use an “out” parameter in place of returning said value in a return statement?
In the case of the NSString initWithContentsOfFile:encoding:error:
method that you cite, an error is handled by returning nil
from the initializer. If this happens, then you can interrogate the error
object (if you provided one) to get details about the error.
NSError *error = nil;
NSString *someString = [[NSString alloc] initWithContentsOfFile:somePath encoding:someEncoding error:&error];
if (!someString) {
// oops - check out the error
} else {
// continue using the string
}
Basically, this method design pattern needs to be in place because the return value can’t be used to provide error information. The return type needs to be the initialized object (or nil
). So the only option for getting more detailed error information is to provide the “out” parameter for the error.
To answer your more general question, the only time I’ve ever used an “out” parameter is when the method needs to return a primary value but you also need a secondary return value.
I can’t think of any void
methods that have “out” parameters (in the iOS SDK).
NSString
gives us a non-error example with the initWithContentsOfFile:usedEncoding:error:
initializer. The usedEncoding
parameter is an “out” parameter. The primary return value is the string. The secondary return value is the encoding that was used to convert the file to a string.
8
IMO (emphasis on the O), you should avoid out parameters whenever you can. I don’t know anything about Objective-C, but if this were Java you’d throw an exception instead of using an out parameter.
Exceptions were created to keep your exceptional logic out of the way of normal flow control. In theory, this makes the code easier to read because it doesn’t have to look like this:
Object something = doSomething();
if (something == isValid())
Object somethingElse = doSomethingElse();
if (somethingElse == isValid())
...
else
//handle what happens when "something else" goes wrong
else
//handle what happens when "something" goes wrong
Instead, it can look like this:
try {
Object something = doSomething();
Object somethingElse = doSomethingElse();
...
} catch (SomethingException e) {
//handle what happens when "something" goes wrong
} catch (SomethingElseException e) {
//handle what happens when "something else" goes wrong
}
What I like about the latter is the typical flow can be read sequentially.
You do something, then you do something else, then you …
With the first example, your error handling is all mixed up together with that logic instead of out of the way in the off chance that it happens.
If Objective-C has exceptions, I think the API would have been better written if it used them in this case. If it doesn’t have them, I assume it at least has Objects. It could have returned some type of response object that you can query to find out if the response had errors.
4
This, to me, just looks like a poor initial API design choice that couldn’t have been fixed without breaking a lot of existing binaries.
It’s pretty much standard that if a function does something that may fail, then the success/failure is flagged through the return value, and if anything else needs to be returned it’s done through a parameter that’s passed by reference.
The way your example is defined does nothing (that I can see) to make the developer’s life easier, unless it’s the type of developer who assumes nothing can go wrong. I’m not sure how static analysis tools would cope with this convention either.
3