您的位置:首页 > 其它

Brief Intro to NSError of Foundation Framework

2013-11-23 11:46 417 查看
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-1. NSError Standard User Info Dictionary Keys
KeyValue 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

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).



Figure
14-1. Testing 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).



Figure
14-2. Testing 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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: