iOS Developer Library

Developer

Start Developing iOS Apps Today

PDF
On This Page

Working with Foundation

As you begin writing Objective-C code for your app, you’ll find many frameworks that you can take advantage of. Of particular importance is the Foundation framework, which provides basic services for all apps. The Foundation framework includes value classes representing basic data types such as strings and numbers, as well as collection classes for storing other objects. You’ll be relying on value and collection classes to write much of the code for your ToDoList app.

image: ../Art/foundation_2x.png

Value Objects

The Foundation framework provides classes that generate value objects for strings, binary data, dates and times, numbers, and other values.

A value object is an object that encapsulates a primitive value (of a C data type) and provides services related to that value. You frequently encounter value objects as the parameters and return values of methods and functions that your app calls. Different parts of a framework—or even different frameworks—can exchange data by passing value objects.

Some examples of value objects in the Foundation framework are:

  • NSString and NSMutableString

  • NSData and NSMutableData

  • NSDate

  • NSNumber

  • NSValue

Because value objects represent scalar values, you can use them in collections and wherever else objects are required. Value objects have an advantage over the primitive types they encapsulate: They let you perform certain operations on the encapsulated value simply and efficiently. The NSString class, for example, has methods for searching for and replacing substrings, for writing strings to files or (preferably) URLs, and for constructing file-system paths.

You create a value object from data of a primitive type. The NSNumber class provides an example of this approach.

  1. int n = 5; // Value assigned to primitive type
  2. NSNumber *numberObject = [NSNumber numberWithInt:n]; // Value object created from primitive type

Later, you can access the encapsulated data from the object.

  1. int y = [numberObject intValue]; // Encapsulated value obtained from value object (y == n)

Most value classes create their instances by declaring both initializers and class factory methods. Class factory methods—implemented by a class as a convenience for clients—combine allocation and initialization in one step and return the created object. For example, the NSString class declares a string class method that allocates and initializes a new instance of the class and returns it to your code.

  1. NSString *string = [NSString string];

In addition to creating value objects and letting you access their encapsulated values, most value classes provide methods for simple operations such as object comparison.

Strings

Objective-C and C support the same conventions for specifying strings: Single characters are enclosed by single quotes, and strings of characters are surrounded by double quotes. But Objective-C frameworks typically don’t use C strings. Instead, they use NSString objects.

The NSString class provides an object wrapper for strings, offering advantages such as built-in memory management for storing arbitrary-length strings, support for different character encodings (particularly Unicode), and utilities for string formatting. Because you commonly use such strings, Objective-C provides a shorthand notation for creating NSString objects from constant values. To use this NSString literal, just precede a double-quoted string with the at sign (@), as shown in the following examples:

  1. // Create the string "My String" plus newline.
  2. NSString *myString = @"My String\n";
  3. // Create the formatted string "1 String".
  4. NSString *anotherString = [NSString stringWithFormat:@"%d %@", 1, @"String"];
  5. // Create an Objective-C string from a C string.
  6. NSString *fromCString = [NSString stringWithCString:"A C string" encoding:NSUTF8StringEncoding];

Numbers

Objective-C offers a shorthand notation for creating NSNumber objects, removing the need to call initializers or class factory methods to create such objects. Simply precede the numeric value with the at sign (@) and optionally follow it with a value type indicator. For example, you can create NSNumber objects encapsulating an integer value and a double value:

  1. NSNumber *myIntValue = @32;
  2. NSNumber *myDoubleValue = @3.22346432;

You can even use NSNumber literals to create encapsulated Boolean and character values.

  1. NSNumber *myBoolValue = @YES;
  2. NSNumber *myCharValue = @'V';

You can create NSNumber objects representing unsigned integers, long integers, long long integers, and float values by appending the letters U, L, LL, and F, respectively, to the notated value. For example, to create an NSNumber object encapsulating a float value, you can write:

  1. NSNumber *myFloatValue = @3.2F;

Collection Objects

Most collection objects in Objective-C code are instances of a basic collection class—NSArray, NSSet, and NSDictionary. Collection classes are used to manage groups of objects, so any item you want to add to a collection must be an instance of an Objective-C class. If you need to add a scalar value, you must first create a suitable NSNumber or NSValue instance to represent it.

Any object you add to a collection will be kept alive at least as long as the collection is kept alive. That’s because collection classes use strong references to keep track of their contents. In addition to keeping track of their contents, each collection class makes it easy to perform certain tasks, such as enumeration, accessing specific items, or finding out whether a particular object is part of the collection.

The contents of the NSArray, NSSet, and NSDictionary classes are set at creation. Because their contents can’t be changed over time, these classes are called immutable. Each one also has a subclass that’s mutable to allow you to add or remove objects at will. Different types of collections organize their contained objects in distinctive ways:

  • NSArray and NSMutableArray. An array is an ordered collection of objects. You access an object by specifying its position (that is, its index) in the array. The first element in an array is at index 0 (zero).

  • NSSet and NSMutableSet. A set stores an unordered collection of objects, with each object occurring only once. You generally access objects in the set by applying tests or filters to objects in the set.

  • NSDictionary and NSMutableDictionary. A dictionary stores its entries as key-value pairs; the key is a unique identifier, usually a string, and the value is the object you want to store. You access this object by specifying the key.

Arrays

An array (NSArray) represents an ordered collection of objects. The only requirement is that each item be an Objective-C object—there’s no requirement for each object to be an instance of the same class.

To maintain order in the array, each element is stored at a zero-based index.

image: ../Art/orderedarrayofobjects.png

Creating Arrays

You create an array through initialization or class factory methods. A variety of different initialization and factory methods are available, depending on the number of objects.

  1. + (id)arrayWithObject:(id)anObject;
  2. + (id)arrayWithObjects:(id)firstObject, ...;
  3. - (id)initWithObjects:(id)firstObject, ...;

Because the arrayWithObjects: and initWithObjects: methods both take a nil-terminated, variable number of arguments, you must include nil as the last value.

  1. id firstObject = @"someString";
  2. id secondObject = @"secondString";
  3. id thirdObject = @"anotherString";
  4. NSArray *someArray =
  5. [NSArray arrayWithObjects:firstObject, secondObject, thirdObject, nil];

The preceding example creates an array with three objects. The first object, firstObject, will have an array index of 0; the last object, thirdObject, will have an index of 2.

It’s possible to create an array literal using a compact syntax.

  1. NSArray *someArray = @[firstObject, secondObject, thirdObject];

When using this syntax, don’t terminate the list of objects with nil—in fact, nil is an invalid value. For example, you’ll get an exception at runtime if you try to execute the following code:

  1. id nilObject = nil;
  2. NSArray *someArray = @[firstObject, nilObject];
  3. // exception: "attempt to insert nil object"

Querying Array Objects

After you’ve created an array, you can query it for information—such as how many objects it has or whether it contains a given item.

  1. NSUInteger numberOfItems = [someArray count];
  2. if ([someArray containsObject:secondObject]) {
  3. ...
  4. }

You can also query the array for an item at a given index. If you attempt to request an invalid index, you’ll get an out-of-bounds exception at runtime. To avoid getting an exception, always check the number of items first.

  1. if ([someArray count] > 0) {
  2. NSLog(@"First item is: %@", [someArray objectAtIndex:0]);
  3. }

This example checks to see whether the number of items is greater than zero. If it is, the Foundation function NSLog logs a description of the first item, which has an index of 0.

As an alternative to using objectAtIndex:, query the array using a subscript syntax, which is just like accessing a value in a standard C array. The previous example can be rewritten like this:

  1. if ([someArray count] > 0) {
  2. NSLog(@"First item is: %@", someArray[0]);
  3. }

Sorting Array Objects

The NSArray class offers a variety of methods to sort its collected objects. Because NSArray is immutable, each method returns a new array containing the items in the sorted order.

For example, you can sort an array of strings by calling compare: on each string.

  1. NSArray *unsortedStrings = @[@"gamma", @"alpha", @"beta"];
  2. NSArray *sortedStrings =
  3. [unsortedStrings sortedArrayUsingSelector:@selector(compare:)];

Array Mutability

Although the NSArray class itself is immutable, it can nevertheless contain mutable objects. For example, if you create an immutable array that contains a mutable string, like this:

  1. NSMutableString *mutableString = [NSMutableString stringWithString:@"Hello"];
  2. NSArray *immutableArray = @[mutableString];

there’s nothing to stop you from mutating the string.

  1. if ([immutableArray count] > 0) {
  2. id string = immutableArray[0];
  3. if ([string isKindOfClass:[NSMutableString class]]) {
  4. [string appendString:@" World!"];
  5. }
  6. }

If you want to add or remove objects from an array after initial creation, use NSMutableArray, which adds a variety of methods to add, remove, or replace one or more objects.

  1. NSMutableArray *mutableArray = [NSMutableArray array];
  2. [mutableArray addObject:@"gamma"];
  3. [mutableArray addObject:@"alpha"];
  4. [mutableArray addObject:@"beta"];
  5. [mutableArray replaceObjectAtIndex:0 withObject:@"epsilon"];

This example creates an array made up of the objects @"epsilon", @"alpha", and @"beta".

It’s also possible to sort a mutable array in place, without creating a secondary array.

  1. [mutableArray sortUsingSelector:@selector(caseInsensitiveCompare:)];

In this case, the contained items are sorted into the ascending, case-insensitive order @"alpha", @"beta", and @"epsilon".

Sets

A set (NSSet) object is similar to an array, but it maintains an unordered group of distinct objects.

image: ../Art/unorderedsetofobjects.png

Because sets don’t maintain order, they offer faster performance than arrays do when it comes to testing for membership.

Because the basic NSSet class is immutable, its contents must be specified at creation, using either an initializer or a class factory method.

  1. NSSet *simpleSet =
  2. [NSSet setWithObjects:@"Hello, World!", @42, aValue, anObject, nil];

As with NSArray, the initWithObjects: and setWithObjects: methods both take a nil-terminated, variable number of arguments. The name of the mutable NSSet subclass is NSMutableSet.

Sets store only one reference to an individual object, even if you try adding an object more than once.

  1. NSNumber *number = @42;
  2. NSSet *numberSet =
  3. [NSSet setWithObjects:number, number, number, number, nil];
  4. // numberSet only contains one object

Dictionaries

Rather than simply maintaining an ordered or unordered collection of objects, a dictionary (NSDictionary) stores objects associated with given keys, which can then be used for retrieval.

The best practice is to use string objects as dictionary keys.

image: ../Art/dictionaryofobjects.png

Although you can use other objects as keys, keep in mind that each key is copied for use by a dictionary and so must support NSCopying. If you want to use key-value coding, however, you must use string keys for dictionary objects. (To learn more, see Key-Value Coding Programming Guide).

Creating Dictionaries

You can create dictionaries through initialization, or you can use class factory methods, like this:

  1. NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
  2. someObject, @"anObject",
  3. @"Hello, World!", @"helloString",
  4. @42, @"magicNumber",
  5. someValue, @"aValue",
  6. nil];

For the dictionaryWithObjectsAndKeys: and initWithObjectsAndKeys: methods, each object is specified before its key, and the list of objects and keys must be nil-terminated.

Objective-C offers a concise syntax for dictionary literal creation.

  1. NSDictionary *dictionary = @{
  2. @"anObject" : someObject,
  3. @"helloString" : @"Hello, World!",
  4. @"magicNumber" : @42,
  5. @"aValue" : someValue
  6. };

For dictionary literals, the key is specified before its object, and the list of objects and keys is not nil-terminated.

Querying Dictionaries

After you’ve created a dictionary, you can ask it for the object stored against a given key.

  1. NSNumber *storedNumber = [dictionary objectForKey:@"magicNumber"];

If the object isn’t found, the objectForKey: method returns nil.

There’s also a subscript syntax alternative to using objectForKey:.

  1. NSNumber *storedNumber = dictionary[@"magicNumber"];

Dictionary Mutability

If you need to add or remove objects from a dictionary after creation, use the NSMutableDictionary subclass.

  1. [dictionary setObject:@"another string" forKey:@"secondString"];
  2. [dictionary removeObjectForKey:@"anObject"];

Representing nil with NSNull

It’s not possible to add nil to the collection classes described in this section because nil in Objective-C means “no object.” If you need to represent “no object” in a collection, use the NSNull class.

  1. NSArray *array = @[ @"string", @42, [NSNull null] ];

With NSNull, the null method always returns the same instance. Classes that behave in this way are called singleton classes. You can check to see whether an object in an array is equal to the shared NSNull instance like this:

  1. for (id object in array) {
  2. if (object == [NSNull null]) {
  3. NSLog(@"Found a null object");
  4. }
  5. }

Although the Foundation framework contains many more capabilities than are described here, you don’t need to know every single detail right away. If you do want to learn more about Foundation, take a look at Foundation Framework Reference. For now, you have enough information to continue implementing your ToDoList app, which you’ll do by writing a custom data class.