Brief Intro to NSError of Foundation Framework

NSError

The Foundation Framework NSError class is used to create error objects. The properties of this class are an error code, the error domain, and a dictionary of user information. The class includes methods for creating and initializing error objects, retrieving error properties, getting a localized error description, and facilitating error recovery.

An error domain is a mechanism used to organize error codes according to a system, subsystem, framework, and so forth. Error domains enable you to identify the subsystem, framework, and so forth, that detected the error. They also help prevent naming collisions between error codes, because error codes between different domains can have the same value. The user info dictionary is anNSDictionary instance that holds error information beyond the code and domain. The types of information that can be stored in this dictionary include localized error information and references to supporting objects. The following statement creates an error object using the NSError class factory method errorWithDomain:code:userInfo:.

NSError *err = [NSError errorWithDomain:NSCocoaErrorDomain
                                   code:NSFileNoSuchFileError
                               userInfo:nil];

This particular error would be created if, for example, a file is not found at the path specified. Notice that the user info dictionary is not provided (the userInfo parameter is set to nil)Listing 14-1creates the same error object, this time with a user info dictionary.

Listing 14-1.  Creating an NSError Object

NSString *desc = NSLocalizedString(@"FileNotFound", @"");
NSDictionary *info = @{NSLocalizedDescriptionKey:desc};
NSError *err = [NSError errorWithDomain:NSCocoaErrorDomain
                                   code:NSFileNoSuchFileError
                               userInfo:info];

Listing 14-1 shows the user info dictionary for this example consists of one entry, the key-value pair for a localized description. The localized string is created using the Foundation NSLocalizedStringfunction. The constant NSLocalizedDescriptionKey is a standard user info dictionary key defined in the NSError class.

The Foundation Framework declares four major error domains:

  • NSMachErrorDomain: OS kernel error codes.
  • NSPOSIXErrorDomain: Error codes derived from standard POSIX-conforming versions of Unix, such as BSD.
  • NSOSStatusErrorDomain: Error codes specific to Apple OS X Core Services and the Carbon framework.
  • NSCocoaErrorDomain: All of the error codes for the Cocoa frameworks (this includes the Foundation Framework and other Objective-C frameworks).

In addition to the major error domains presented here, there are also error domains for frameworks, groups of classes, and even individual classes. The NSError class also enables you to create your own error domain when creating and initializing an NSError object. As mentioned previously, Listing 14-1uses the constant NSLocalizedDescriptionKey. The NSError class defines a set of common user info dictionary keys that can be used to create the key-value pairs for the user info dictionary. These keys are listed in Table 14-1.

Table 14-1NSError Standard User Info Dictionary Keys

Key Value Description
NSLocalizedDescriptionKeyLocalized string representation of the error.
NSFilePathErrorKeyThe file path of the error.
NSStringEncodingErrorKeyAn NSNumber object containing the string encoding value.
NSUnderlyingErrorKeyThe error encountered in an underlying implementation (which caused this error).
NSURLErroKeyAn NSURL object.
NSLocalizedFailureReasonErroryKeyLocalized string representation of the reason that caused the error.
NSLocalizedRecoverySuggestionErrorKeyLocalized recovery suggestion for the error.
NSLocalizedRecoveryOptionsErrorKeyNSArray containing the localized titles of buttons for display in an alert panel.
NSRecoveryAttempterErrorKeyAn object that conforms to theNSErrorRecoveryAttempting protocol.
NSHelpAnchorErrorKeyLocalized string representation of help information for a help button.
NSURLErrorFailingURLStringNSURL object containing the URL that caused the load to fail.
NSURLErrorFailingURLStringErrorKeyString for the URL that caused the load to fail.
NSURLErrorFailingURLPeerTrustErrorKeySecTrustRef object representing the state of a failed SSL handshake.

Using Error Objects

OK, so now you know how to create NSError objects, but how do you use them? In general, there are two scenarios for obtaining NSError objects:

  • Delegation: An error object passed as a parameter to a delegation method that you implement.
  • Indirection: An error object retrieved via indirection from a method that your code invokes.

The delegation pattern is a design pattern whereby an object (the delegator) delegates one or more tasks to another delegate object. The tasks are encapsulated in the method(s) of the delegate object. When necessary, the delegator invokes the appropriate method on the delegate object, providing any required parameters. Many Foundation Framework classes implement the delegation pattern to enable custom error handling. The delegating Foundation object invokes a method on a delegate object (custom code that you implement) that includes an error object as a parameter.

The NSURLConnectionDelegate protocol declares a delegation method (connection:didFailWithError:) that returns an error object:

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

This protocol is used for asynchronously loading a URL request via an NSURLConnection object. Your code implements a delegate object that conforms to this protocol and sets it as the delegate of theNSURLConnection object. Your code then loads the URL request asynchronously, and if an error occurs, the delegating (NSURLConnection) object invokes the connection:didFailWithError:method on your delegate object.

A common Objective-C programming convention for methods that return an error object is to make this the last parameter of the method and to specify the type of this parameter as a pointer to an error object pointer (also known as double-indirection). The following example declares a method named getGreeting that has an error object of type NSError as its parameter.

- (NSString *)getGreeting(NSError **error);

This approach enables the called method to modify the pointer the error object points to, and if an error occurs, return an error object specific to the method call.

A return value for the method is required. It is either an object pointer or a Boolean value. Your code invokes such a method by including a reference to an error object as its last parameter, or providingNULL as the last parameter if you don’t need to access the error. After the method is invoked, the result is inspected. If the value is NO or nil, then the error object should be processed; else no error object was returned. Many Foundation classes have methods that return an error object by indirection. In addition, your classes can use this convention to implement methods that return an error object.

Listing 14-2 depicts a class named FileWriter, which declares a method that (indirectly) returns an error object.

Listing 14-2.  FileWriter Interface with Method That Returns an NSError Object

@interface FileWriter : NSObject
+ (BOOL) writeData:(NSData *)data toFile:(NSString *)path error:(NSError **)err;
@end

For your code to call this method on a FileWriter object, it must first declare an NSError object, as shown in Listing 14-3, and then check the return value to see if an error occurred.

Listing 14-3.  Invoking FileWriter Method That Returns an NSError Object

NSError *writeErr;
NSData *greeting = [@"Hello, World" dataUsingEncoding:NSUTF8StringEncoding];
BOOL success = [FileWriter writeData:greeting
                              toFile:NSTemporaryDirectory()
                               error:&writeErr];
if (!success)
{
  // Process error
  ...
}

Now you’ll create a couple of example programs that perform error handling for error objects passed by delegation methods and error objects obtained via indirection.

Handling Delegation Method Errors

Now you’ll implement a program that performs error handling for a delegate method. In Chapter 11, you implemented a program, NetConnector, which demonstrates URL loading using the Foundation Framework NSURLConnection class. Here you’ll update this program to handle errors when loading a URL.

In Xcode, open the NetConnector project by selecting Open image NetConnector.xcodeproj from the Xcode File menu. The source code for the project consists of three files that implement theNetConnector class and the main function. Let’s start by making some updates to the NetConnectorclass. Select the NetConnector.m file in the navigator pane, and then update the NetConnector implementation (updates in bold), as shown in Listing 14-4.

Listing 14-4.  NetConnector Class Implementation, Updated to Handle Errors

#import "NetConnector.h"

@interface NetConnector()

@property NSURLRequest *request;
@property BOOL isFinished;
@property NSURLConnection *connector;
@property NSMutableData *receivedData;

@end

@implementation NetConnector

- (id) initWithRequest:(NSURLRequest *)request
{
  if ((self = [super init]))
  {
    _request = request;
    _finishedLoading = NO;
     
    // Create URL cache with appropriate in-memory storage
    NSURLCache *URLCache = [[NSURLCache alloc] init];
    [URLCache setMemoryCapacity:CACHE_MEMORY_SIZE];
    [NSURLCache setSharedURLCache:URLCache];
     
    // Create connection and begin downloading data from resource
    _connector = [NSURLConnection connectionWithRequest:request delegate:self];
  }
  return self;
}

- (void) reloadRequest
{
  self.finishedLoading = NO;
  self.connector = [NSURLConnection connectionWithRequest:self.request
                                                 delegate:self];
}

#pragma mark -
#pragma mark Delegate methods

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
  NSString *description = [error localizedDescription];
  NSString *domain = [error domain];
  NSInteger code = [error code];
  NSDictionary *info = [error userInfo];
  NSURL *failedUrl = (NSURL *)[info objectForKey:NSURLErrorFailingURLErrorKey];
  NSLog(@"\n*** ERROR ***\nDescription-> %@\nURL-> %@\nDomain-> %@\nCode-> %li",
        description, failedUrl, domain, code);
  self.finishedLoading = YES;
}

- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
  if (self.receivedData != nil)
  {
    [self.receivedData setLength:0];
  }
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
  NSURLResponse *response = [cachedResponse response];
  NSURL *url = [response URL];
  if ([[url scheme] isEqualTo:HTTP_SCHEME])
  {
    NSLog(@"Downloaded data, caching response");
    return cachedResponse;
  }
  else
  {
    NSLog(@"Downloaded data, not caching response");
    return nil;
  }
}

- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
   if (self.receivedData != nil)
   {
     [self.receivedData appendData:data];
   }
   else
   {
     self.receivedData = [[NSMutableData alloc] initWithData:data];
   }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  NSUInteger length = [self.receivedData length];
  NSLog(@"Downloaded %lu bytes from request %@", length, self.request);
   
  // Loaded data, set flag to exit run loop
  self.finishedLoading = YES;
}

@end

An NSURLConnection sends the connection:didFailWithError: message to its delegate object if the connection doesn’t load its request successfully. As shown in Listing 14-4, the NetConnector class is set as the delegate for its NSURLConnection object, and hence its connection:didFailWithError: method will be invoked on request load errors. The method implementation retrieves the properties of the NSError object: description, domain, code, and user info. The URL of the failed load request is stored in the user info dictionary. Its key is NSURLErrorFailingURLErrorKey, one of the NSError standard user info dictionary keys listed in Table 14-1. The method logs the values of this data to the output pane.

Next, you will update the main() function, but before you do that, you need to create a page with a valid URL that cannot be loaded. Doing this forces an error to occur when you attempt to load the page using an NSURLConnection object. Open an OS X terminal window and enter the Unix commands shown in Listing 14-5.

Listing 14-5.  Using the Mac OS X Terminal Utility to Create a File

touch /tmp/ProtectedPage.html
chmod u-r /tmp/ProtectedPage.html

The touch command creates a new, empty file. As shown in Listing 14-6, the full path for the file is/tmp/ProtectedPage.html. The chmod u-r command removes read access to the file for the current user. The corresponding URL for this resource is file:///tmp/ProtectedPage.html. OK, now that this resource is configured properly, let’s update the main() function. Select the main.m file in the navigator pane, and then update the main() function, as shown in Listing 14-6.

Listing 14-6.  NetConnector main( ) Function Implementation

#import <Foundation/Foundation.h>
#import "NetConnector.h"

#define INDEX_URL       @"file:///tmp/ProtectedPage.html"

int main(int argc, const char * argv[])
{
  @autoreleasepool
  {
    // Retrieve the current run loop for the connection
    NSRunLoop *loop = [NSRunLoop currentRunLoop];

    // Create the request with specified cache policy, then begin downloading!
    NSURLRequest *request = [NSURLRequest
                             requestWithURL:[NSURL URLWithString:INDEX_URL]
                                cachePolicy:NSURLRequestReturnCacheDataElseLoad
                            timeoutInterval:5];
    NetConnector *netConnect = [[NetConnector alloc] initWithRequest:request];
     
    // Loop until finished loading the resource
    while (!netConnect.finishedLoading &&
           [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
     
    // Zero out cache
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
  }
  return 0;
}

The key change in the main() function is the URL. It is updated to the URL for the resource that you created earlier, file:///tmp/ProtectedPage.html. The code will attempt to asynchronously load this resource (with an NSURLConnection object) using the NetConnector initWithRequest: method. As shown in Listing 14-4, the NetConnector class implements the connection:didFailWithError:method to handle errors loading a URL.

Now save, compile, and run the updated NetConnector program and observe the messages in the output pane (as shown in Figure 14-1).

9781430250500_Fig14-01.jpg

Figure 14-1Testing the NSError portion of the updated NetConnector project

The messages in the output pane show that the NSURLConnection failed to load the URL, and hence sent the connection:didFailWithError: message to its delegate object—in this case, theNetConnector instance. As shown in the method implementation of Listing 14-4, the error description, failed URL, error code, and domain are logged to the output pane. OK, great. Now that you’ve got that under your belt, let’s implement a program that returns an error object via indirection.

Creating Errors Objects via Indirection

Now you will create a program that demonstrates error handling for a Foundation Framework object. In Xcode, create a new project by selecting New Project . . . from the Xcode File menu. In the New Project Assistant pane, create a command-line application. In the Project Options window, specifyFMErrorObject for the Product Name, choose Foundation for the Project Type, and select ARC memory management by selecting the Use Automatic Reference Counting check box. Specify the location in your file system where you want the project to be created (if necessary select New Folderand enter the name and location for the folder), uncheck the Source Control check box, and then click the Create button.

In the Xcode project navigator, select the main.m file and update the main() function, as shown inListing 14-7.

Listing 14-7.  FMErrorObject main( ) Function Implementation

#import <Foundation/Foundation.h>

#define FILE_PATH       @"/tmp/NoSuchFile.txt"

int main(int argc, const char * argv[])
{
  @autoreleasepool
  {
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    NSError *fileErr;
    BOOL success = [fileMgr removeItemAtPath:FILE_PATH error:&fileErr];
    if (!success)
    {
      NSString *description = [fileErr localizedDescription];
      NSString *domain = [fileErr domain];
      NSInteger code = [fileErr code];
      NSDictionary *info = [fileErr userInfo];
      NSURL *failedPath = (NSURL *)[info objectForKey:NSFilePathErrorKey];
      NSLog(@"\n*** ERROR ***\nDescription-> %@\nPath-> %@\nDomain-> %@\nCode-> %li",
            description, failedPath, domain, code);
    }
  }
  return 0;
}

Logically, the code uses an NSFileManager instance method to remove a file from the file system. If an error occurs when invoking the method, it returns an error object via indirection that describes the error, along with an appropriate return result. As shown in Listing 14-7, the file begins by defining a variable, FILE_PATH, which represents the full path of a file on the local file system (in order to test error object creation and processing make sure that this file, /tmp/NoSuchFile.txt, doesn’t exist). The main() function begins by creating a FileManager object and an NSError pointer. It then attempts to delete the file by invoking the NSFileManager removeItemAtPath:error: method. As the error parameter is not NULL, an NSError object will be returned if an error occurs when invoking this method.

Next, a conditional expression is performed using the Boolean result of this method; if the returned value is NO, then an error occurred trying to remove the file and the body of the conditional expression is executed. This code retrieves the properties of the NSError object: description, domain, code, and user info. The full path of the file that couldn’t be removed is stored in the user info dictionary. Its key is NSFilePathErrorKey, one of the NSError standard user info dictionary keys listed in Table 14-1. The method logs the values of this data to the output pane.

Now save, compile, and run the FMErrorObject program and observe the messages in the output pane (as shown in Figure 14-2).

9781430250500_Fig14-02.jpg

Figure 14-2Testing NSError by indirection in the FMErrorObject project

The messages in the output pane show that the NSFileManager object failed to remove the file, and hence set an error object (via indirection) in the error parameter of the removeFileWithPath:error:method, and set its return result to NO. The error description, file path, error code, and domain are logged to the output pane. Perfect. Now that you understand how to retrieve and process error objects, let’s examine the Foundation Framework support for error recovery.

Error Recovery

The NSError class provides a mechanism for recovering from errors. TheNSErrorRecoveryAttempting informal protocol provides methods that are implemented to perform error recovery. An object that adopts this protocol must implement at least one of its two methods for attempting recovery from errors. The user info dictionary of an NSError object that supports error recovery must contain, at a minimum, the following three entries:

  • The recovery attempter object (retrieved using the keyNSRecoveryAttempterErrorKey)
  • The recovery options (retrieved using the keyNSLocalizedRecoveryOptionsErrorKey)
  • A localized recovery suggestion string (retrieved using the keyNSLocalizedRecoverySuggestionErrorKey)

The recovery attempter may implement any logic appropriate for error recovery. Note that the error recovery functionality is only available on the Apple OS X platform.

Error Responders

The Application Kit provides APIs and mechanisms that can be used to respond to errors encapsulated in NSError objects. The NSResponder class defines an error responder chain used to pass events and action messages up the view hierarchy. It includes methods to display information in the associatedNSError object, and then forwards the error message to the next responder. This enables each object in the hierarchy to handle the error appropriately, perhaps by adding additional information pertaining to the error. Note that the error responder functionality is available only on the Apple OS X platform.

NSError Codes

The Foundation Framework defines a number of standard NSError codes as Foundation constants. These errors are enumerations defined for the following NSError class error domains, as follows:

  • Cocoa (NSErrorCocoaDomain)
  • URL Loading System (NSURLDomain)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值