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.Newin Go.Sentinel errors: These are often represented as constant
NSStringvalues 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
userInfodictionary under theNSUnderlyingErrorKeykey.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.