Brief Intro to Foundation Functions

来源:互联网 发布:软件系统测试方法 编辑:程序博客网 时间:2024/04/29 22:29
Foundation Functions

The Foundation Framework defines a number of functions and function-like macros. The Apple Foundation Functions Reference provides a definitive guide for these functions. Here you’ll examine Foundation functions that perform the following tasks:

  • Assertions and logging
  • String localization
  • Numerical operations and byte ordering
  • Runtime operations

Assertions

An assertion is a statement placed in code to check the existence of a condition. Assertions are used to provide runtime verification of assumptions that, if not present, should cause the program to terminate. Coding with assertions is one of the quickest and most effective ways to detect and correct bugs. Assertions can also document the logic of your program, thereby enhancing maintainability.

Each assertion contains a Boolean expression that you believe will be true when the assertion executes. If it is not true, the system will throw an error. By verifying that the expression is indeed true, the assertion confirms your assumptions about the behavior of your program, increasing your confidence that the program is free of errors.

The following are examples of situations where you would use assertions:

  • Internal invariant: An assumption concerning program behavior. This assumption is often indicated with comments in code, but it can be documented and validated at runtime with an assertion.
  • Control-flow invariant: An assumption concerning control flow, specifically locations in your code that should not be reached.
  • Precondition: A condition that must be true before a method is invoked.
  • Postcondition: A condition that must be true after a method is invoked.
  • Class invariant: A condition that must be true for each class instance.

An example of an internal invariant is the conditional expression inListing 13-1.

Listing 13-1.  Example of a Conditional Expression with an Internal Invariant

if (value >= 0){  ...}else{  // value must be negative  ...}

As shown in Listing 13-1, these assumptions are often indicated with comments in code, but can be documented and validated at runtime with an assertion. The code excerpt fromListing 13-1 is updated in Listing 13-2 to express the invariant with an assertion.

Listing 13-2.  Example of an Internal Invariant Expressed with an Assertion

if (value >= 0){  ...}else{  NSAssert((value < 0), @"Value not valid here, should be negative");  ...}

A common type of precondition is a conditional check of the input parameters for a method (or function). For public methods, preconditions are explicitly checked in code and throw the appropriate exception if the condition is not satisfied, hence they should not be checked with assertions. Assertions can be used to check preconditions for private methods and functions.

Assertions can be used to test postconditions for both public and private methods/functions.

A class invariant constrains the properties (i.e., internal state) and behavior of each instance of a class. Assertions can be used to define class invariants in a manner similar to that used to express the other situations discussed earlier.

Assertion Macros

The Foundation assertion functions are, in fact, macros that enable the creation of assertions in Objective-C code. Each assertion macro evaluates a condition and, if the condition evaluates to false, passes a string (and possibly additional printf-style arguments formatted into the string) describing the failure to anNSAssertionHandler instance. NSAssertionHandler is a Foundation Framework class used to handle false assertions, and each program thread has its ownNSAssertionHandler object. So, when the condition of a Foundation assertion macro evaluates to false, it passes the condition describing the error to theNSAssertionHandler object for the current thread. This object in turn logs the error and then raises an exception (specifically, anNSInternalIncosistencyException) that causes the program to terminate.NSAssertionHandler instances are generally not created programmatically, but rather by an assertion function.

There are multiple Foundation assertion macros to support the creation of assertions within Objective-C methods and functions. Each assertion macro has at least one argument: a conditional expression that evaluates to true or false. Most also include an argument that contains a format string describing the error condition.The assertion macros support the substitution of zero to five arguments in a format string. The complete list of Foundation assertions functions is provided inTable 13-1.

Table 13-1.Foundation Assertion Functions

Foundation FunctionDescriptionNSAssertGenerates an assertion for an Objective-C method if a given condition evaluates toNO (false). Its arguments include the conditional expression and a format string (with no format specifier) that describes the error.NSAssert1Similar to NSAssert, its arguments include the conditional expression, a format string (with one format specifier), and an argument to be inserted into the format string.NSAssert2, NSAssert3, NSAssert4, NSAssert5Similar to NSAssert, its arguments include the conditional expression, a format string (with two, three, four, or five format specifiers), and two, three, four, or five arguments to be inserted into the format string.NSParameterAssertGenerates an assertion for the parameter of an Objective-C method. Its arguments are the conditional expression for a parameter.NSCAssertGenerates an assertion for an Objective-C function: if a given condition evaluates to NO (false). Its arguments include the conditional expression and a format string (with no format specifier) that describes the error.NSCAssert1Similar to NSCAssert, its arguments include the conditional expression, a format string (with one format specifier), and an argument to be inserted into the format string.NSCAssert2,NSCAssert3, NSCAssert4,NSCAssert5Similar to NSCAssert, its arguments include the conditional expression, a format string (with two, three, four, or five format specifiers), and two, three, four, or five arguments to be inserted into the format string.NSCParameterAssertGenerates an assertion for the parameter of an Objective-C function. Its arguments are the conditional expression for a parameter.

The following statement uses theNSAssert function to assert that the value of the variable namedage is within the prescribed range.

NSAssert((age > 0) && (age <=18), @"Variable age not within prescribed range");

In the next statement, the same assertion is performed again, this time using theNSAssert1 function to log the value of the variable if the assertion fails.

NSAssert1((age > 0) && (age <=18),  @"Value %d for age not within prescribed range", age);

Assertions can be used to check parameters of private methods or functions. Parameter checking for public methods/functions is normally part of the corresponding published API; hence, these should be checked whether or not assertions are enabled.Listing 13-3 uses the NSParameterAssert function to assert that the parameters for the methodencodeArchive:toFile: have valid values.

Listing 13-3.  Asserting Method Parameter Values Using NSParameterAssert

- (BOOL) encodeArchive:(id)objectGraph toFile:(NSString *)file{  NSParameterAssert(file != nil);  NSParameterAssert([objectGraph conformsToProtocol:@protocol(NSCoding)]); ...}

You can disable assertions in your code by defining the preprocessor macro;NS_BLOCK_ASSERTIONS, for example.

#define NS_BLOCK_ASSERTIONS

Logging

The Foundation Framework includes two functions,NSLog and NSLogv, for logging output to the system log facility. In Xcode, error messages from these functions are displayed in the Xcode output pane. By now you have become familiar with the NSLog function through the example source code provided in this book, so here you’ll examine theNSLogv function.

NSLogv, as withNSLog, logs an error message to the system log facility.It differs from NSLog in that it supports a variable argument list. The declaration for the NSLog function is

void NSLogv(NSString *format, va_list args);

The variable format is a format string, and the variableargs is a data type used for providing a list of (variable) arguments to functions. So you may be wondering, when would you useNSLogv?If you have a method or function that takes a variable argument list, thenNSLogv can be used to log messages with this list. For example, thevariadic function printArgs declared as follows:

void printArgs(int numArgs, ...);

logs a number of function arguments to the system log facility, where the arguments are a variable list (specified by the... variable argument symbol) and the number of arguments from this list to be logged (specified by thenumArgs function argument). Given this function declaration,Listing 13-4 demonstrates how this function is implemented using the NSLogv function.

Listing 13-4.  Using NSLogv to Log a variadic Argument List

void printArgs(int numArgs, ...){  va_list args;  va_start(args, numArgs);  va_end(args);  NSMutableString *format = [[NSMutableString alloc] init];  [format appendString:@"Arguments: "];  for (int ii=0; ii<numArgs-1; ii++)  {    [format appendString:@"%@, "];  }  if (numArgs > 1)  {    [format appendString:@"%@"];  }  NSLogv(format, args);}

If this function is invoked as follows:

printArgs(3, @"Hello", @"Objective-C", @"World!");

It logs to the console the following message:

Arguments: Hello, Objective-C, World!

Bundles

The Foundation Framework includes several function macros for retrieving localized strings. They are used to load strings from a program’s strings files. Before you begin examining these macros, let’s take a moment to discuss internationalization and localization.

Internationalization is the process of designing an application so that it can serve different languages and regions (i.e., locales) without being changed.Localization is the adaptation of an internationalized application to a local market. Localization can be applied to many parts of an application, including its visible text, icons and graphics, views, and data.

Localizing Strings

Localizing visible text is a key part of the localization process.Strings in code that require localization must be extracted, localized, and reinserted back in the code in order to display properly in the specified locale. Apple provides a tool, genstrings, which can be used to facilitate text localization. Thegenstrings tool searches your code for uses of the localization function macros and uses the information they contain to build the initial set ofstrings files (resource files that contain localizable strings) for your application. For example, if your source code contains a statement that utilizes the localization function macro NSLocalizedString to return a localized version of the stringYes, as follows:

NSString *yes = NSLocalizedString(@"Yes", @"Word for yes");

the genstrings tool will parse this statement and use it to build a strings file. The genstrings tool can parse C and Objective-C source code files with the.c or .m filename extensions. You can also specify the output directory where genstrings places the resulting strings files. In most cases, you would want to specify the directory containing the project resources for your development language.

The following example uses the genstrings tool to parse all Objective-C source files in the current directory and store the resulting strings file in theen.lproj subdirectory, which must already exist.

genstrings -o en.lproj *.m

The first time you run the genstrings tool, it creates a set of new strings files for you. Subsequent executions on the sme file(s) replace the contents of those strings files with the current string entries found in your source code.

Localized Strings Functions

Once you have localized strings in a strings file, the Foundation localized string function macros can be used to retrieve the appropriate localized string for a specified locale. The Foundation Framework defines four function macros for getting localized strings, shown inTable 13-2.

Table 13-2.Foundation Localized String Functions

Foundation FunctionDescriptionNSLocalizedStringRetrieves a localized version of a string on the main application bundle in the default strings table.NSLocalizedStringFromTableRetrieves a localized version of a string on the main application bundle in the specified strings table.NSLocalizedStringFromTableInBundleRetrieves a localized version of a string on the specified application bundle and strings table.NSLocalizedStringWithDefaultValueRetrieves a localized version of a string on the specified application bundle and strings table, using the default value if the key is not found.

The localized string functions depend on anNSBundle class instance. Each function invokes anNSBundle object’s localizedStringForKey:value:table: method, providing the function arguments as parameters for the method.

So, the localization functions both provide expressions that can be parsed by the genstrings tool to create an application’s strings file, and retrieve a localized version of a string. They also let you associate translation comments with each entry.

For example, if your program has astrings table in a file named Localizable.strings that includes the following strings:

"Yes" = "Yes""No" = "No"

the NSLocalizedStringFromTable function would retrieve the string Yes with the following statement.

NSString *yes = NSLocalizedStringFromTable(@"Yes", @"Localizable.strings",                @"Word for yes");

Conversely, if the Localizable.strings file for the French locale contained the values

"Yes" = "Oui""No" = "Non"

Then the preceding statement would retrieve the stringOui.

Decimal Numbers and Byte Ordering

The Foundation Framework includes functions for performing decimal number operations. The arithmetic operations supported include decimal arithmetic, rounding, comparison, copy, normalizing, and string representations.

The functions take one or moreNSDecimal numbers as arguments, along with (potentially) other parameters.NSDecimal is a Foundation data typeused to describe a decimal number.

NSDecimalAdd,NSDecimalSubtract, NSDecimalMultiply, and NSDecimalDivide perform the associated arithmetic operations onNSDecimal instances. NSDecimalMultiplyByPowerOf10 multiplies a number by an input power of 10. Each takes as an input argument anNSRoundingMode Foundation constant that specifies how the result is rounded. The available rounding modes are:

  • NSRoundDown: Rounds the returned value down.
  • NSRoundUp: Rounds the returned value up.
  • NSRoundPlain: Rounds to the closest possible return value.When the value is halfway between two positive numbers, it rounds up. When the value is halfway between two negative numbers, it rounds down.
  • NSRoundBankers: Rounds to the closest possible return value. When the value is halfway between two numbers, it returns the value whose last digit is even.

Rounding can be explicitly performed using theNSDecimalRound function, or automatically performed for theNSDecimal arithmetic functions if the result has more digits than the maximum number of significant digits allowed (38).

NSDecimalPower raises a number to the specified power.NSDecimalNormalize and NSDecimalCompact both change the representation of a decimal number. NSDecimalCompact formats a decimal so that it takes up the minimum amount of memory. All of theNSDecimal functions expect compact decimal arguments.NSDecimalNormalize updates the format of its two decimal arguments such that they have the same exponent.NSDecimalAdd and NSDecimalSubtract invoke NSDecimalNormalize.NSDecimalRound rounds the input decimal according to the inputNSRoundingMode constant.

NSDecimalCopy copies the value of a decimal number to anotherNSDecimalNumber instance. NSDecimalCompare compares two decimal numbers, returning the result of the comparison as anNSComparisonResult value (a Foundation constant) that can have one of the following values:

  • NSOrderedDescending: The left operand is greater than the right operand.
  • NSOrderedAscending: The left operand is less than the right operand.
  • NSOrderedSame: The two operands are equal.

Using the NSDecimal Functions

Now you’ll create a program that demonstrates the use of the Foundation decimal arithmetic functions. In Xcode, create a new project by selectingNew image Project . . . from the Xcode File menu. In theNew Project Assistant pane, create a command-line application. In the Project Options window, specifyDecimalAddition for the Product Name, choose Foundation for the Project Type, and select ARC memory management by selecting theUse Automatic Reference Counting check box. Specify the location in your file system where you want the project to be created (if necessary selectNew Folder and enter the name and location for the folder), uncheck theSource Control check box, and then click the Create button.

OK, the DecimalAddition project is created. Now select the main.m file and update themain() function, as shown in Listing 13-5.

Listing 13-5.  Performing Decimal Arithmetic Using the NSDecimalAdd and NSDecimalRound Functions

#import <Foundation/Foundation.h>int main(int argc, const char * argv[]){  @autoreleasepool  {    // Create two NSDecimal numbers using the NSDecimalNumber class    NSDecimal dec1 = [[NSDecimalNumber decimalNumberWithString:@"1.534"]                       decimalValue];    NSDecimal dec2 = [[NSDecimalNumber decimalNumberWithString:@"2.011"]                       decimalValue];         // Declare result and rounded result variables for the calculations    NSDecimal result;    NSDecimal roundedResult;         // Now perform the decimal addition    NSDecimalAdd(&result, &dec1, &dec2, NSRoundPlain);    NSLog(@"Sum = %@", NSDecimalString(&result, nil));         // Demonstrate rounding the result using the available rounding modes    NSDecimalRound(&roundedResult, &result, 2, NSRoundUp);    NSLog(@"Sum (round up) = %@", NSDecimalString(&roundedResult, nil));    NSDecimalRound(&roundedResult, &result, 2, NSRoundDown);    NSLog(@"Sum (round down) = %@", NSDecimalString(&roundedResult, nil));    NSDecimalRound(&roundedResult, &result, 2, NSRoundPlain);    NSLog(@"Sum (round plain) = %@", NSDecimalString(&roundedResult, nil));    NSDecimalRound(&roundedResult, &result, 2, NSRoundBankers);    NSLog(@"Sum (round bankers) = %@", NSDecimalString(&roundedResult, nil));  }  return 0;}

As shown in Listing 13-5, the NSDecimalNumber class is used to create two decimal numbers of theFoundation type NSDecimal. TheNSDecimalAdd function is then used to add the two numbers. Because the number of digits in the result is less than the maximum allowed, no rounding is performed and the result printed by the NSLog function is 3.545. Next, the NSDecimalRound function is used to explicitly round the result to two digits after the decimal point; each of the available rounding modes is used to demonstrate how they affect the result. When you compile and run the program, you should observe messages in the output pane comparable to those shown inFigure 13-1.

9781430250500_Fig13-01.jpg

Figure 13-1.DecimalAddition program output

As you can see inFigure 13-1, the selected rounding mode affects the result as explained earlier.

The NSDecimalNumber class provides methods that duplicate much of the functionality of theNSDecimal functions; hence, you have the choice of which to use to perform decimal arithmetic. TheNSDecimal functions provide better performance and lower memory utilization than theNSDecimalNumber class. However, the NSDecimalNumber class performs these decimal operations using objects, and enables you to directly store the results of these operations in an object-oriented collection like an instance ofNSArray or NSDictionary.

Byte Ordering

The Foundation Framework includes a set of functions that perform byte ordering; specifically, these functions can be used to swap the order of bytes of a number as stored in memory. Byte ordering is important when a binary file created on one computer is read on another computer with a different byte order.Big endian machines (such as a Motorola 680x0 CPU) store data with the most significant byte in the lowest memory address.A little endian machine (such as an Intel CPU) stores the most significant byte in the highest memory address.For example, the decimal value of 4132 (hexadecimal 1024) would be stored as shown inTable 13-3.

Table 13-3.Byte Ordering for Big Endian and Little Endian Machines

table

In order for binary files to be successfully transferred and processed between these big and little endian machines, the byte ordering of the data needs to be swapped. The NSSwap byte ordering functions perform big to little and little to big endian byte ordering for the following data types: short, int, long, long long, float, and double. The NSSwapHost functions swap from the current endian format to that specified for the input data type. The NSHostByteOrder function determines the host endian format. It returns a Foundation constant value ofNS_LittleEndian or NS_BigEndian.

Interacting with the Runtime

The Foundation Framework includes several functions that utilize the Objective-C runtime library. These functions enable dynamic operations.

TheNSGetSizeAndAlignment function is used to get the size of a character array in bytes. The following functions retrieve a string representation of a corresponding Objective-C type:

  • NSStringFromSelector
  • NSStringFromProtocol
  • NSStringFromClass

The following statement retrieves a text string (sayHello:) for a selector specified with the @selector directive, and assigns this string to the variablemethodName.

NSString *methodName = NSStringFromSelector(@selector(sayHello:));

The next set of functions perform the converse operation; they retrieve an Objective-C type from a string representation:

  • NSSelectorFromString
  • NSProtocolFromString
  • NSClassFromString

The following statement dynamically retrieves a selector from the input string.

SEL methodSel = NSSelectorFromString(@"sayHello:");

File Paths

A set of Foundation functions provides operations for managing file paths, including retrieving the user name, home directory, temporary directory, and directory search paths. The following statement demonstrates use of theNSFullUserName function to retrieve the full name of the current user.

NSString *fullName = NSFullUserName();

The NSUserName function, in comparison, returns the logon name of the current user.

The following statement demonstrates use of theNSHomeDirectory function to retrieve the home directory for the current user.

NSString *homeDir = NSHomeDirectory();

The NSHomeDirectoryForUser function retrieves the home directory for the user specified in the input argument.

TheNSTemporaryDirectory function retrieves the full path to the temporary directory for the current user.

The NSSearchPathForDirectoriesInDomains function returns a list of directory search paths for the specified directories in the specified domains. The syntax for the function is

NSSearchPathForDirectoriesInDomains(  NSSearchPathDirectory directory,  NSSearchPathDomainMask domainMask,  BOOL expandTilde);

The NSSearchPathDirectory is a Foundation constant that specifies a variety of directories in the file system, for example theDocuments directory. The NSSearchPathDomainMaskis a Foundation constant used to specify the domain for the search; for example, the user’s home directory. TheBOOL argument is set to YES if the paths returned should be expanded. Listing 13-6 demonstrates the use of the function to retrieve the documents directoryfor the current user.

Listing 13-6.  Retrieving the Documents Directory Using the NSSearchPathForDirectoriesInDomains Function

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,                                                     NSUserDomainMask, YES);NSString *documentsDirectory = paths[0];

Geometry

The Foundation Framework includes functions that assist in the performance of geometric calculations on points, ranges, rectangles, and sizes. A point represents a location in two-dimensional Euclidean geometry with x and y coordinates. A range represents a one-dimensional quantity beginning at a specified location of a specified length. Asize represents a two-dimensional quantity of specified width and height. The point, range, and size Foundation functions perform operations to create, retrieve, test for equality, and return string representations of these quantities. The Foundation data types NSPoint represents points,NSRange represents ranges, andNSSize represents sizes. The following statement creates a newNSPoint from the specified values.

NSPoint point = NSMakePoint(0, 5);

The Foundation framework also includes numerous functions for manipulating rectangles, including operations to create, get, get components, query, test for equality, and return string representations. There are also functions that perform geometric operations (test for intersection, union, divide, point in rectangle, etc.). The Foundation data typeNSRect represents rectangles. The following statement creates a rectangle at point {0, 0} and size [5, 10].

NSRect rect1 = NSMakeRect(0.0, 0.0, 5.0, 10.0);

The next set of statements creates a new rectangle at point {2.5, 5} and size [5, 10], and then calculates the intersection of the two rectangles.

NSRect rect2 = NSMakeRect(2.5, 5.0, 5.0, 10.0);NSRect rect3 = NSIntersectionRect(rect1, rect2);
原创粉丝点击