Errors in Objective-C
#import <Foundation/Foundation.h>
// By convention, errors are returned as the last parameter in Objective-C methods
// that can fail. We use NSError objects to represent errors.
NSError* f(int arg, int* result) {
if (arg == 42) {
// NSError's factory method to create a basic error with a given description
return [NSError errorWithDomain:@"com.example" code:1
userInfo:@{NSLocalizedDescriptionKey: @"can't work with 42"}];
}
// A nil value in the error position indicates that there was no error
*result = arg + 3;
return nil;
}
// Sentinel errors are typically defined as constant NSString values
NSString* const ErrOutOfTea = @"no more tea available";
NSString* const ErrPower = @"can't boil water";
NSError* makeTea(int arg) {
if (arg == 2) {
return [NSError errorWithDomain:@"com.example" code:2
userInfo:@{NSLocalizedDescriptionKey: ErrOutOfTea}];
} else if (arg == 4) {
// In Objective-C, we can't directly wrap errors, but we can create a new error
// that references the original one in its userInfo dictionary
NSError* powerError = [NSError errorWithDomain:@"com.example" code:3
userInfo:@{NSLocalizedDescriptionKey: ErrPower}];
return [NSError errorWithDomain:@"com.example" code:4
userInfo:@{NSLocalizedDescriptionKey: @"making tea",
NSUnderlyingErrorKey: powerError}];
}
return nil;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray* numbers = @[@7, @42];
for (NSNumber* number in numbers) {
int result;
NSError* error = f([number intValue], &result);
if (error) {
NSLog(@"f failed: %@", error.localizedDescription);
} else {
NSLog(@"f worked: %d", result);
}
}
for (int i = 0; i < 5; i++) {
NSError* error = makeTea(i);
if (error) {
// In Objective-C, we check for specific errors by comparing their domain and code,
// or by checking the error's localizedDescription
if ([error.localizedDescription isEqualToString:ErrOutOfTea]) {
NSLog(@"We should buy new tea!");
} else if ([error.localizedDescription isEqualToString:ErrPower]) {
NSLog(@"Now it is dark.");
} else {
NSLog(@"unknown error: %@", error.localizedDescription);
}
continue;
}
NSLog(@"Tea is ready!");
}
}
return 0;
}
In Objective-C, error handling is typically done through the use of NSError
objects. Here’s how the concepts translate:
Error return values: Instead of returning multiple values, Objective-C methods that can fail usually take an
NSError**
as their last parameter and return a boolean indicating success or failure.Creating errors: We use
[NSError errorWithDomain:code:userInfo:]
to create error objects, similar toerrors.New
in Go.Sentinel errors: These are often represented as constant
NSString
values in Objective-C.Error wrapping: Objective-C doesn’t have a direct equivalent to Go’s error wrapping, but we can achieve a similar effect by creating a new error and including the original error in its
userInfo
dictionary under theNSUnderlyingErrorKey
key.Checking for specific errors: Instead of
errors.Is
, we typically compare error domains and codes, or check the error’slocalizedDescription
.
The structure of the program remains similar, demonstrating error creation, checking, and handling in an Objective-C context. Note that Objective-C uses manual memory management or Automatic Reference Counting (ARC), which is why we wrap the main code in an @autoreleasepool
block.
To compile and run this Objective-C program:
$ clang -framework Foundation errors.m -o errors
$ ./errors
f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.
This example demonstrates how to work with errors in Objective-C, including creating, returning, and handling different types of errors.