The Foundation framework is the bread and butter in the toolbox of an iOS developer. It provides the NSObject
root class and a large number of fundamental building blocks for iOS development, from classes for numbers and strings, to arrays and dictionaries. The Foundation framework might seem a bit dull at first, but it harnesses a lot of power and is indispensable when developing iOS applications.
A Word About Core Foundation
In the previous article, I briefly mentioned Core Foundation and its relation to Foundation. Even though we will not explicitly use the Core Foundation framework in the rest of this series, it's a good idea to be familiar with it and to know how it differs from the Foundation framework, which you'll use extensively.
While the Foundation framework is implemented in Objective-C, the Core Foundation framework is based on the C language. Despite this difference, the Core Foundation framework does implement a limited object model. This object model allows for the definition of a collection of opaque types that are often referred to as objects—despite the fact that they are, strictly speaking, not objects.
The primary goal of both frameworks is similar, enabling sharing of data and code between the various libraries and frameworks. Core Foundation also includes support for internationalization. A key component of this support is provided through the CFString
opaque type, which efficiently manages an array of Unicode characters.
As I mentioned previously, toll-free bridging literally bridges the gap between both frameworks by enabling the substitution of Cocoa objects for Core Foundation objects in function parameters and vice versa.
It's important to note that Automatic Reference Counting (ARC) does not manage Core Foundation "objects", which means that you are responsible for managing memory when working with Core Foundation "objects". There is a great article by Mike Ash about Automatic Reference Counting and how to use ARC with Core Foundation and toll-free bridging.
Visit the Mac Developer Library for a complete list of the opaque types included in the Core Foundation framework.
Practice, Practice, Practice
Learning a new skill is best done through practice so create a new project in Xcode and select the Command Line Tool project template as we did earlier in this series. The Command Line Tool template can be found in the Application category in the OS X section. Click Next to continue.
Name the new project Foundation and enter an organization name and company identifier. For this project, it's key to set the project type to Foundation. Tell Xcode where you want to save the project and hit Create.
Test Drive
Our playground for the rest of this article will be main.m and the Xcode console window. Open main.m by selecting it in the Project Navigator in the left sidebar and make sure that the console window is visible by clicking the middle button of the View control in the Xcode toolbar.
Click the Run button in the top left to build and run the current scheme. If all went well, you should see Hello, World! appear in the console window at the bottom.
The Foundation Framework
The Foundation framework is much more than a collection of classes for working with numbers, strings, and collections (arrays, dictionaries, and sets). It also defines dozens of protocols, functions, data types, and constants.
In the rest of this article, I will primarily focus on the classes that you'll use most often when developing iOS applications. However, I will also briefly talk about three key protocols defined by the Foundation framework, NSObject
, NSCoding
, and NSCopying
.
Header Files
As you already know, the header file of a class defines its interface. Does that mean that you have to import the header file of each Foundation class that you plan to use? The answer is yes and no.
It is indeed necessary to import the header file of a class before you can use it. You do so by adding an import statement as we saw earlier in this series. However, the Foundation framework provides a convenient way to facilitate this process. The only file you need to import is Foundation.h like shown in the following code snippet.
#import <Foundation/Foundation.h>;
Behind the scenes, the Foundation framework imports all the necessary header files to give you access to every class, protocol, function, data type, and constant of the Foundation framework.
Importing
When you create a new project in Xcode and you set the project type to Foundation, Xcode will:
- link the project against the Foundation framework
- add the above import statement to main.m
- add the above import statement to the project's precompiled header file (*.pch)
Open main.m to verify this and expand the frameworks folder in the Project Navigator by clicking the small triangle on its left. I will revisit the precompiled header file and its purpose when we take a look at the UIKit framework.
Protocols
Several languages, such as Perl, Python, and C++, provide support for multiple inheritance, which means that a class can descend—be a subclass of—more than one class.
Even though Objective-C does not provide support for multiple inheritance, it does support multiple inheritance through specification in the form of protocols. What does this mean? Instead of inheriting from a class, a protocol defines a list of methods that classes implement if they conform to the protocol.
A protocol can have required and optional methods. If a class does not implement all the required methods of a protocol, the compiler will throw an error.
The benefits of protocols are manifold. When a class adopts or conforms to a protocol, the class is expected to implement the (required) methods declared in the protocol.
Objective-C protocols are very similar to interfaces in Java. This means that a protocol can be used to declare the interface to an object without revealing the class of the object.
Multiple inheritance has its benefits, but it most certainly has its downsides. The advantage of protocols is that unrelated classes can still share similar behavior through the use of protocols.
NSObject
In addition to the NSObject
root class, the Foundation framework also defines a NSObject
protocol. Objects conforming to the NSObject
protocol can be asked about their class and superclass, can be compared with other objects, and respond to self
as we saw in the article about Objective-C. This is only a small subset of the behavior added to objects conforming to the NSObject
protocol.
NSCoding
Objects conforming to the NSCoding
protocol can be encoded and decoded. This is necessary for objects that need to be archived or distributed. Object archival takes place when an object or object graph is stored to disk, for example.
NSCopying
The NSCopying
protocol declares only one method, copyWithZone:
. If a class is to support copying objects, it needs to conform to the NSCopying
protocol. Copying an object is done by sending it a message of copy
or copyWithZone:
.
NSObject
The NSObject
class is the root class of the vast majority of the Objective-C class hierarchies. Do you remember that we instantiated an instance of the Book
class earlier in this series? We sent the Book
class a message of alloc
and we sent the resulting object a message of init
. Both methods are declared in the NSObject
class.
By inheriting from the NSObject
root class, objects know how to behave as Objective-C objects and how to interface with the Objective-C runtime. It shouldn't be a surprise that NSObject
conforms to the NSObject
protocol that we saw earlier.
Remove the NSLog
statement in main.m and paste the following code snippet in its place.
NSObject *myFirstObject = [[NSObject alloc] init]; NSLog(@"Class > %@", [myFirstObject class]); NSLog(@"Superclass > %@", [myFirstObject superclass]); NSLog(@"Conforms to Protocol > %i", [myFirstObject conformsToProtocol:@protocol(NSObject)]);
We start by instantiating an instance of NSObject
and store a reference to it in the myFirstObject
variable. In the second line, we log the class name of the new object to the console. The class
method, returns an instance of NSString
, a string object, which is the reason that we use the %@
format specifier in the NSLog
statement.
Next, we ask myFirstObject
for its superclass and end by verifying that myFirstObject
conforms to the NSObject
protocol. Are you confused by @protocol(NSObject)
? This is nothing more than a reference to the NSObject
protocol.
Click Run and inspect the output in the console window. Are you surprised by the output? Because NSObject
is a root class, it doesn't have a superclass.
NSNumber
The NSNumber
class is a utility class that manages any of the basic numeric data types. It is a subclass of the NSValue
class, which provides an object oriented wrapper for scalar types as well as pointers, structures, and object ids. The NSNumber
class defines methods for retrieving the value it stores, for comparing values, and also for retrieving a string representation of the stored value.
Keep in mind that the value retrieved from an NSNumber
instance needs to be consistent with the value that was stored in it. The NSNumber
class will attempt to dynamically convert the stored value to the requested type, but it goes without saying that there are limitations inherent to the data types that NSNumber
can manage.
Let me illustrate this with an example. Add the following code snippet to main.m.
NSNumber *myNumber = [NSNumber numberWithDouble:854736e+13]; NSLog(@"Double Value > %f", [myNumber doubleValue]); NSLog(@"Float Value > %f", [myNumber floatValue]); NSLog(@"Int Value > %i", [myNumber intValue]);
We start by creating a new NSNumber
instance by passing a double
value to numberWithDouble:
. Next, we retrieve the stored value using three different NSNumber
methods. The results don't reflect the value stored in myNumber
for obvious reasons.
The lesson is simple, be consistent when using NSNumber
by keeping track of the type that is stored in the NSNumber
instance.
NSString
Instances of the NSString
class manage an array of unichar
characters forming a string of text. The subtle but important difference with a regular C string, which manages char
characters, is that a unichar
character is a multibyte character.
As its name implies, a unichar
character is ideally suited for handling Unicode characters. Due to this implementation, the NSString
class provides out-of-the-box support for internationalization.
I want to emphasize that the string managed by a NSString
instance is immutable. This means that once the string is created, it cannot be modified. Developers coming from other languages, such as PHP, Ruby, or JavaScript, might be confused by this behavior.
The Foundation framework also defines a mutable subclass of NSString
, NSMutableString
, which can be modified after it's been created.
There are various ways to create string objects. The simplest way to create a string object is by calling the string
method on the NSString
class, which returns an empty string object. Take a look at the class reference of NSString
for a complete list of initializers.
Another common path for creating string objects is through string literals as shown in the example below. In this example, a literal string is assigned to the someString
variable. At compile time, the compiler will replace the string literal with an instance of NSString
.
NSString *string1 = @"This is a string literal.";
The NSString
class has a multitude of instance and class methods for creating and manipulating strings and you will rarely, if ever, feel the need to subclass NSString
.
Let's explore NSString
and its mutable subclass, NSMutableString
, by adding the following snippet to main.m.
NSString *string1 = @"This is a string literal."; NSString *string2 = [[NSString alloc] initWithFormat:@"Strings can be created many ways."]; NSMutableString *mutableString = [[NSMutableString alloc] initWithString:string1]; [mutableString appendFormat:@" %@", string2]; NSLog(@"%@", mutableString);
We start by creating a string object using a string literal. In the second line, we create a second string by using one of the specialized initializers that NSString
provides. A mutable string is then created by passing the first string as an argument. To illustrate that mutable strings can be modified after creation, string2
is appended to the mutable string and logged to the console window.
NSArray
and NSSet
The NSArray
class manages an immutable, ordered list of objects. The Foundation framework also defines a mutable subclass of NSArray
, NSMutableArray
. The NSArray
class behaves very much like a C array with the difference that an instance of NSArray
manages objects. In addition, NSArray
declares a wide range of methods that facilitate working with arrays, such as methods for finding and sorting objects in the array.
It's important to understand that instances of NSArray
, NSSet
, and NSDictionary
can only store objects. This means that it's not possible to store scalar types, pointers, or structures in any of these collection classes—or their subclasses—the compiler will throw an error if you do. The solution is to wrap scalar types, pointers, and structures in an instance of NSValue
or NSNumber
as we saw earlier in this article.
Add the following code snippet to main.m to explore NSArray
and its mutable counterpart, NSMutableArray
.
NSArray *myArray = [NSArray arrayWithObjects:@"Bread", @"Butter", @"Milk", @"Eggs", nil]; NSLog(@"Number of Elements > %li", [myArray count]); NSLog(@"Object at Index 2 > %@", [myArray objectAtIndex:2]); NSMutableArray *myMutableArray = [NSMutableArray arrayWithObject:[NSNumber numberWithInt:265]]; [myMutableArray addObject:[NSNumber numberWithInt:45]]; NSLog(@"Mutable Array > %@", myMutableArray);
In the first line, we create an array by using the arrayWithObjects:
class method. This method accepts a variable number of arguments—objects—with the last argument being nil
—which is not included in the array. In the second and third line, we query the array for the number of objects in the array and the object stored at index 2
respectively.
Because NSMutableArray
inherits from NSArray
, it behaves in much the same way as NSArray
. The main difference is that objects can be added and removed from the array after it's been created.
Before moving on, I want to say a few words about NSSet
. This class is similar to NSArray
, but the key differences are that the collection of objects that a set manages is unordered and duplicates are not allowed.
The advantage of NSSet
is that querying its objects is faster if you only need to know if an object is contained in the set. The Foundation framework also defines NSOrderedSet
. Instances of this class have the benefits of NSSet
, but also keep track of the position of each object.
NSDictionary
Like arrays, dictionaries are a common concept in most programming languages. In Ruby, for example, they are referred to as hashes. The basic concept is easy, a dictionary manages a static collection of key-value pairs or entries.
As in Ruby hashes, the key of an entry doesn't need to be a string object per se. It can be any type of object that conforms to the NSCopying
protocol as long as the key is unique in the dictionary. In most cases, though, it's recommended to use string objects as keys.
Like arrays, dictionaries cannot store a null value. If you want to represent a null value, then you can use NSNull
. The NSNull
class defines a singleton object that is used to symbolize null values in arrays, dictionaries, and sets.
The singleton pattern is an important pattern in many programming languages. It limits the instantiation of a class to one object. You will deal with singleton objects frequently when developing iOS applications.
Like NSArray
, the Foundation framework defines a mutable subclass of NSDictionary
, NSMutableDictionary
. There are various ways to instantiate a dictionary. Take a look at the following code snippet.
NSString *keyA = @"myKey"; NSString *keyB = @"myKey"; NSDictionary *myDictionary = [NSDictionary dictionaryWithObject:@"This is a string literal" forKey:keyA]; NSLog(@"%@", [myDictionary objectForKey:keyB]);
We first declare two separate string objects containing the same string. In the third line, we instantiate a dictionary by calling the dictionaryWithObject:forKey:
method on the NSDictionary
class.
Next, we ask the dictionary for the object associated with the contents of keyB
and log it to the console.
It's important to pay attention to the details. Even though we used keyA
as the key of the key-value pair and keyB
as the key to fetch the value or object of the key-value pair, the dictionary gave us the correct object. The NSDictionary
class is smart enough to know that we want the object associated with string myKey
. What does this mean? Even though the objects keyA
and keyB
are different objects, the string that they contain is the same and that is precisely what the NSDictionary
class uses to reference the object.
The following code fragment shows that a dictionary can contain another dictionary—or array—and it also shows how to work with mutable dictionaries.
NSMutableDictionary *myMutableDictionary = [NSMutableDictionary dictionary]; [myMutableDictionary setObject:myDictionary forKey:@"myDictionary"]; NSLog(@"%@", myMutableDictionary);
Objective-C Literals
Earlier in this article, I introduced you to Objective-C string literals, such as @"This is a string literal."
. They take the form of a C string literal prefixed with an @
sign. As you probably know by now, the @
sign indicates that we are entering Objective-C territory.
An Objective-C literal is nothing more than a block of code that references an Objective-C object. With the release of Xcode 4.5, you can now also use Objective-C literals for NSNumber
, NSArray
, and NSDictionary
. Take a look at the following code snippet to see how this works.
NSNumber *oldNumber1 = [NSNumber numberWithBool:YES]; NSNumber *newNubmer1 = @YES; NSNumber *oldNumber2 = [NSNumber numberWithInt:2147]; NSNumber *newNubmer2 = @2147; NSArray *oldArray = [NSArray arrayWithObjects:@"one", @"two", @"three", nil]; NSArray *newArray = @[@"one", @"two", @"three"]; NSDictionary *oldDictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:12345] forKey:@"key"]; NSDictionary *newDictionary = @{@"key": @12345};
Not only are Objective-C literals cool and sexy, they also make your code more readable. Mark Hammonds wrote a tutorial about Objective-C literals. Read Mark's post for a more complete overview of Objective-C literals.
Logging with NSLog
In this article, we have repeatedly used the NSLog
function, which is defined by the Foundation framework. NSLog
accepts a variable number of arguments with the first argument being a string literal. The string literal can contain format specifiers that are replaced by the extra arguments passed to the NSLog
function.
NSLog(@"%@ - %i - %f", @"an object", 3, 3.14);
Visit the Mac Developer Library for a complete list of the format specifiers that can be used.
Conclusion
Even though we have covered a lot of ground in this article, we've barely scratched the surface of what the Foundation framework has to offer.
It isn't necessary to know the details of every class or function defined in the Foundation framework to get started with iOS development, though. You'll learn more about the Foundation framework as you explore the iOS SDK.
In the next article, we'll explore the UIKit framework and I will also discuss the ins and outs of an iOS application.
Comments