Getting Started with Applications: Chapter 2 - iPhone Open Application Development

by Jonathan Zdziarski

If you’re new to Mac, you might be surprised to find that applications don’t come in the form of .exe files. The excellent design for which Apple is known in its hardware and graphics extends into its software architecture as well, and includes the way applications are laid out in the file system. The same strategy used in Apple desktop systems carries over into the iPhone.

iPhone Open Application Development book cover

This excerpt is from iPhone Open Application Development. This new edition of iPhone Open Application Development covers the latest version of the open toolkit -- now updated for Apple's iPhone 2.x software and iPhone 3G -- and explains in clear language how to create applications using Objective-C and the iPhone API.

buy button

Apple has adopted the practice of creating modular, self-contained applications with their own internal file resources. As a result, installing most applications is as easy as simply dragging them into your applications folder; deleting them as easy as dragging them into the trash. In this chapter, the structure of applications on the iPhone will be explained. You’ll also get up and running with the free open source tool chain used to build executables, and you’ll learn how to install applications on your iPhone. Finally, you’ll be introduced to the Objective-C language and enough of its idiosyncrasies to make an easy transition from C or C++.

Anatomy of an Application

Apple came up with an elegant way to contain applications in their operating system. As OS X is a Unix-based platform, Apple wanted to make it adhere to basic Unix file conventions, and so the resource forks of olde were no longer sufficient (or efficient for that matter). The challenge was to design a structure that would allow an application to remain self-contained while surviving on a file system that didn’t believe in cheapening its architecture with proprietary workarounds. The result was to treat an application as a bundle inside a directory and use standard APIs to access resources, execute binaries, and read information about the application.

If you look inside any Mac application, you’ll find that the .app extension denotes not a file, but a directory. This is the application’s program directory. Inside it is an organized structure containing resources the application needs to run, information about the application, and the application’s executable binaries. A compiler doesn’t generate this program directory structure, but only builds the executable binaries. So to build a complete application, it’s up to the developer to create a skeleton structure that will eventually host the binary and all of its resources.

The program directory for an iPhone application is much less structured than desktop Mac applications. In fact, all of the files used by the application are in the root of the .app program folder:

drwxr-xr-x    root    admin    Terminal.app/
    -rw-r--r--    root    admin    Default.png
    -rw-r--r--    root    admin    Info.plist
    -rwxr-xr-x    root    admin    Terminal
    -rw-r--r--    root    admin    icon.png
    -rw-r--r--    root    admin    pie.png

The above reflects a very basic iPhone application called MobileTerminal. MobileTerminal is an open source terminal client for the iPhone, allowing the user to pull up a shell and work in a Unix environment (which also must be installed as third-party software). MobileTerminal illustrates all of the major components of an iPhone application:

Terminal.app

The directory that all of the application’s resources reside in.

Default.png

A PNG image (Portable Network Graphics file). When the user starts the application, the iPhone animates it to give the appearance that it’s zooming to the front of the screen. This is done by loading Default.png and scaling it up until it fills the screen. This 320×480 image zooms to the front and remains on the screen until the application finishes launching, at which point it serves as the background for whatever user interface elements are drawn on the screen. Applications generally use a solid black or white background.

Info.plist

A property list containing information about the application. This includes the name of its binary executable and a bundle identifier, which is used by the SpringBoard application to launch it. You’ll see an example property list later on in this section.

Terminal

The actual binary executable that is called when the application is launched. This is what your compiler outputs when it builds your application. Your makefile can copy your binary into the application folder when doing a production build. This chapter will provide an example of this process.

icon.png

An image forming the application’s icon on the SpringBoard (the iPhone’s desktop application). SpringBoard isn’t concerned with the size of the file and will attempt to draw the image outside of its icon space if it is large enough. Most icons are generally 60×60 pixels.

pie.png

An image resource used by the MobileTerminal application. There are many methods provided by the iPhone framework to fetch resources, and most of them accept only a filename instead of a path. The file supplied to these methods must therefore be stored directly in the program directory. This is consistent with Apple’s effort to keep applications self-contained.

Creating an Application Skeleton

The first thing you’ll need to do before building an application is to put together a skeleton .app directory to contain it.[1] The skeleton will provide all of the information necessary for the iPhone to acknowledge the existence of your application so it can be run from the SpringBoard.

This book presents many fully functional code examples, and in order to properly run them, you’ll need to build an example skeleton called MyExample.app. Creating the directory is easy enough:

$ mkdir MyExample.app

Next, write a property list to describe the application and how to launch it. The Info.plist file expresses the property list in XML and should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>MyExample</string>
    <key>CFBundleIdentifier</key>
    <string>com.oreilly.www.iphone.examples</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>1.0</string>
</dict>
</plist>

The most important options above have been bolded. These are the values for CFBundleExecutable and CFBundleIdentifier. The CFBundleExecutable property specifies the filename of the binary executable within the folder. This is the file that gets executed when your application is launched—the output from your compiler. In this example, the filename is the same as the application’s name, but this isn’t absolutely necessary.

The CFBundleIdentifier property specifies a unique identifier by which your application is known. The application layer of the iPhone is more concerned about addressing your application as a whole rather than the binary itself. Whenever SpringBoard (or another application) launches MyExample, it will be referenced using this identifier. The name must be unique among all other applications on the iPhone. It’s common practice to incorporate the URL of your website to ensure it’s unique.

The application’s icon.png and Default.png files are also copied in. If these are left out, the iPhone will use the worst-looking images possible to serve as default images for both. We’ll leave the files out of our example to show you what we mean. Make sure to create and include images with these names when you publish your own applications to make them look professional.

Our skeleton is now good enough to run examples. In the next section, you’ll install the tool chain on your desktop, after which you can get started compiling example applications. In the coming chapters, you’ll build many examples. After each has been built, the binary executable MyExample will need to be copied into your program folder. Your completed application will look like this:

drwxr-xr-x    root    admin    MyExample.app/
-rw-r--r--    root    admin    Info.plist
-rwxr-xr-x    root    admin    MyExample

The examples provided in this book generally do not need any additional resources, so images and sounds will be necessary only when the example calls for them. When they do, however, you’ll copy the files required into the MyExample.app directory. Most examples make use of existing files on the iPhone to avoid filling up the book with binary code.

Building the Free Tool Chain

As we discussed in Chapter 1, Breaking Into and Setting Up the iPhone, the iPhone began life as a closed platform. This originally meant that no developer tools were publicly available to build iPhone-native applications. There has been much speculation about whether Apple secretly hoped the community would break into the phone, thus bolstering its status among the geek community. Over the first few months of the iPhone’s life, this is exactly what happened. The open source community successfully cracked the phone and began writing a tool chain to build applications. It has since been released as free software. The tool chain consists of a cross-compiler, a linker, an assembler, a C hook into the assembler called Csu, and class headers for Objective-C frameworks generated by a tool called class-dump.

Today, the tool chain has undergone many improvements by Jay Freeman (also known as saurik) and is available in two forms. A desktop version of the tool chain uses a cross-compiler, which is a compiler that runs on one machine (namely, your desktop) but builds executables that can run on a different machine (the ARM processor in an iPhone). A native compiler can also be installed directly on the iPhone, allowing you to build applications without installing any special software on your desktop.

The commands and pathnames provided throughout this book presume that you’ve used the procedures from this chapter to build and/or install the tool chain. The tool chain is updated periodically as new versions of it are released, so its setup can sometimes change. The latest instructions for building the tool chain on the desktop can be found on Jay Freeman’s site at http://www.saurik.com.

The tool chain builds and installs into /toolchain by default. All of the examples provided in this book will presume that this is where you’ve installed it. If you’ve built the tool chain before, or are just concerned about modifying files there, you’ll want to move your current /toolchain out of the way and start with a fresh directory.

Warning

If you have a previous version of the tool chain, newer versions may not build correctly. To ensure that you start with a clean installation, move your old copy out of the way.

What You’ll Need

If you’re looking to install the tool chain directly on the iPhone, the only thing you’ll need is a jailbroken iPhone with the community installer, Cydia, runing on it. To install the tool chain, simply launch Cydia and then choose the Development section from the Sections list. Scroll down and select iPhone 2.0 Toolchain, then tap the Install button. The tool chain will then be installed on the iPhone, and you can invoke it using the standard gcc command. You can now skip the rest of this section.

Building on the desktop is more involved. While there are some unofficial binary distributions of the tool chain floating around the Internet, you’ll be building it from sources in this section. The following are requirements for building from sources.

Supported desktop platform

The first thing you’ll need is a desktop platform that is supported. Platforms currently supported by the tool chain are:

  • Mac OS X 10.4 Intel or PPC

  • Mac OS X 10.5 Intel

  • Ubuntu Feisty Fawn, Intel

  • Ubuntu Gutsy Gibbon, Intel

  • Fedora Core, Intel

  • Gentoo Linux 2007.0, x86_64

  • Debian 2.6.18

  • CentOS 4

Other platforms follow the same basic steps as these. Official tool chain instructions can be found at http://www.saurik.com.

High-speed Internet connection

The tool chain is considerable in size—even just the sources. Unless you want to be sitting around for a few days, you’ll likely want to download the sources over a high-speed connection. If you don’t have one, it might be a good idea to perform the installation from a library or local coffee shop.

Open source tools

The next things you’ll need are the necessary open source tools installed on your desktop:

  • bison (v1.28 or later)

  • flex (v2.5.4 or later)

  • gcc (the GNU compiler that handles C, C++, and Objective-C)

  • git (a source code control utility)

If you’re missing any of these tools, download and install them before proceeding. On the Mac, all but git are included with Xcode tools, and you’ll want to install or upgrade to the latest version of Xcode before proceeding. Most other operating systems provide them as optional components in their distribution.

Tip

Xcode tools can be downloaded from Apple’s website at http://developer.apple.com/tools/xcode/.

iPhone filesystem

You’ll need a copy of your iPhone’s filesystem, specifically, the libraries and frameworks. For dramatic effect, and because our lawyers make us, we’ll display this general disclaimer:

Warning

Installing the tool chain requires that you copy libraries from your iPhone to your desktop. Check your local, state, and federal laws to ensure this is legal where you reside.

Having installed SSH onto the iPhone in Chapter 1, Breaking Into and Setting Up the iPhone, use the following commands to download the files you need into a folder called /toolchain/sys:

$ sudo -s
# mkdir -p /toolchain/sys/
# cd /toolchain/sys/
# mkdir -p ./System/Library ./usr
# scp -r root@iphone:/System/Library/Frameworks/ ./System/Library
# scp -r root@iphone:/System/Library/PrivateFrameworks/ ./System/Library
# scp -r root@iphone:/usr/lib ./usr

Compiling the Tool Chain

Before you get started, you’ll set a few environment variables to specify where the tool chain is to be installed. To install into /toolchain, use the following paths. You may also change these to your liking. Make sure you place the downloaded libraries and frameworks into whatever you specify as ${sysroot}:

# export target=arm-apple-darwin9
# export prefix=/toolchain/pre
# export sysroot=/toolchain/sys
# export PATH="${prefix}/bin":$PATH
# export cctools=/toolchain/src/cctools
# export gcc=/toolchain/src/gcc
# export csu=/toolchain/src/csu
# export build=/toolchain/bld

Once you’ve got all of the variables exported, you’re ready to start building.

Step 1: Install the Csu

The Csu provides C hooks into assembly’s “start” entry point, and sets up the stack so that your program’s main( ) function can be called. It’s essentially glue code:

# mkdir -p ${csu}
# cd "${csu}"
# svn co http://iphone-dev.googlecode.com/svn/trunk/csu .
# cp -R *.o "${sysroot}"/usr/lib
# cd "${sysroot}"/usr/lib
# chmod 644 *.o
# cp -Rf crt1.o crt1.10.5.o
# cp -Rf dylib1.o dylib1.10.5.o

Step 2: Build and install cross-compiler tools

The following commands build and install the cross-compiler components of the tool chain. These are specific to Mac OS X, so be sure to read the official documentation if you’re using a different platform:

# rm -rf "${cctools}"
# svn co http://iphone-dev.googlecode.com/svn/branches/odcctools-9.2-ld "${cctools}"
# mkdir -p "${build}"
# cd "${build}"
# mkdir cctools-iphone
# cd cctools-iphone
# CFLAGS=-m32 LDFLAGS=-m32 "${cctools}"/configure \
    --target="${target}" \
    --prefix="${prefix}" \
    --disable-ld64
# make
# make install

Step 3: Install the system headers

The system headers found in your Xcode SDK are shared by your desktop and the iPhone platform, but some pre-compiler macros are architecture-specific. Because the iPhone’s architecture is different from the desktop’s, these headers need to be installed to work for the iPhone. Issue the following commands to install an iPhone-custom set of headers into your newly created tool chain. These will be based on Xcode’s headers:

# cd "${build}"
# svn co http://iphone-dev.googlecode.com/svn/branches/include-1.2-sdk include
# cd include
# ./configure --prefix="${sysroot}"/usr
# bash install-headers.sh

Step 4: Build and install LLVM

After the headers are installed, the final step is to build the LLVM compiler. The LLVM (Low Level Virtual Machine) framework provides a standard infrastructure for building compilers. It provides the necessary hooks and APIs to build a standardized compiler without having to rewrite all of the basic components of a compiler. Issue the following statements to compile and install a release build of the LLVM compiler:

# rm -rf "${gcc}"
# git clone git://git.saurik.com/llvm-gcc-4.2 "${gcc}"
# mkdir -p "${build}"
# cd "${build}"
# mkdir gcc-4.2-iphone
# cd gcc-4.2-iphone
# "${gcc}"/configure \
    --target="${target}" \
    --prefix="${prefix}" \
    --with-sysroot="${sysroot}" \
    --enable-languages=c,c++,objc,obj-c++ \
    --with-as="${prefix}"/bin/"${target}"-as \
    --with-ld="${prefix}"/bin/"${target}"-ld \
    --enable-wchar_t=no \
    --with-gxx-include-dir=/usr/include/c++/4.0.0
# make -j2
# make install
# mkdir -p "${sysroot}"/"$(dirname "${prefix}")"
# ln -s "${prefix}" "${sysroot}"/"$(dirname "${prefix}")"

Building and Installing Applications

Now that the tool chain has been installed, the next step is to learn how to use it. There are two essential ways to build an executable: the command line or a makefile.

The examples in this book are simple enough that they can be built using the command line. The tool chain is compliant to standard compiler arguments, and should be familiar if you’ve ever used gcc in the past. You’ll want to make sure /toolchain/pre/bin is in your path before you try to use the cross-compiler:

$ export PATH=$PATH:/toolchain/pre/bin

The anatomy of a typical command-line compiler is:

$ arm-apple-darwin9-gcc -o MyExample MyExample.m -lobjc \
-framework CoreFoundation -framework Foundation \
-march=armv6 -mcpu=arm1176jzf-s
arm-apple-darwin9-gcc

The name of the cross-compiler itself. This is located in /toolchain/pre/bin, so be sure you’ve added it to your path.

-o MyExample

Tells the compiler to output the compiled executable to a file named MyExample.

MyExample.m

The name of the source file(s) being included in the program, separated by spaces. The .m extension tells the compiler that the sources are written in Objective-C.

-lobjc

Tells the compiler to link in the tool chain’s Objective-C messaging library, which is needed by all iPhone applications. This library glues C-style function calls to Objective-C messages, among other things.

-framework CoreFoundation -framework Foundation

Two of the base frameworks to be linked into the application. Depending on what components of the operating system are being used in the code, different frameworks provide different layers of functionality. You’ll be introduced to many different frameworks throughout this book.

-march=armv6 -mcpu=arm1176jzf-s

Sets the correct architecture and CPU type of the executable.

The command line will suffice for most small applications and examples, but for larger applications, it makes sense to write a makefile. A makefile is a simple text file that acts as a manifest for building applications. It is used by a program called make, which is a portable build utility included with most development kits. The make program is responsible for calling the compiler (and linker) and passing them whatever flags and parameters are needed. Makefiles are logical ways to lay out the composition of an application. They also allow the developer to easily clean up the object files in a directory, create application packages, and perform a number of other tasks useful to building applications.

The previous command-line example could be rewritten as a makefile like the one below, named Makefile, and placed into the source directory:

CC = /toolchain/pre/bin/arm-apple-darwin9-gcc
LD = $(CC)
LDFLAGS = -lobjc \
          -framework CoreFoundation \
          -framework Foundation
CFLAGS = -march=armv6 -mcpu=arm1176jzf-a

all:       MyExample

MyExample: MyExample.o
           $(LD) $(LDFLAGS) -o $@ $^

%.o:       %.m
           $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

%.o:       %.c
           $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

%.o:       %.cpp
           $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

Warning

All indentations are actually tabs. Tabs must be used in order for the makefile to work properly.

Once the makefile file is in place, the application’s executable can be built with one simple command:

$ make

In addition to building an application, functionality can be added to copy the application’s executable into the program folder skeleton you made:

package:
          cp -p MyExample ./MyExample.app/

With this added to the makefile, you can run make package to automatically set up your .app directory.

Another popular use for makefiles is to clean the directory so that it can be sent to someone else. You can even tell the makefile to delete the executable that was copied into the program folder:

clean:
           rm -f *.o *.gch
           rm -f ./MyExample.app/MyExample

Installing an Application

Once an application has been built, it can be installed by copying the entire program directory into the /Applications folder on the iPhone. Using the SSH server you set up in Chapter 1, Breaking Into and Setting Up the iPhone, you can do this over WiFi:

$ scp -r MyExample.app root@iphone:/Applications

Before the iPhone will recognize the application, either the iPhone must be powered down and rebooted, or the SpringBoard application must be restarted on the iPhone itself. Log in to the iPhone using SSH and execute the following command to restart SpringBoard:

$ killall SpringBoard

Once restarted, you should see the application on the SpringBoard.

If you are running iPhone firmware v2.0 or greater, you’ll need to digitally sign your application in order for it to run. This can be done right on the iPhone by installing the link-identity tool. This is done through Cydia, or you can use Cydia’s apt-get tool from the command line on the iPhone, if you’re running SSH:

# apt-get install ldid

Once you’ve installed ldid, sign your application using the command below:

# ldid -S /Applications/MyExample.app/MyExample

You can now tap the icon to launch your application from the SpringBoard.

Transitioning to Objective-C

Objective-C was written in the early 1980s by scientist and software engineer Brad Cox. It was designed as a way of introducing the capabilities of the Smalltalk language into a C programming environment. A majority of the iPhone’s framework libraries are written in Objective-C, but because the language was designed to accommodate the C language, you can use C and C++ in your application as well. Objective-C is used primarily on Mac OS X and GNUstep (a free OpenStep environment). Many languages, such as Java and C#, have borrowed from the Objective-C language. The Cocoa framework makes heavy use of Objective-C on the Mac desktop, which carries over onto the iPhone.

If you’ve developed on the Mac OS X desktop before, you’re already familiar with Objective-C, but if the iPhone is your first Apple platform, then you’re likely transitioning from C or C++. This section will cover some of the more significant differences between these languages. If you have a prior background in C or C++, this should be enough to get you up and writing code using the examples in this book as a guide.

Messaging

The first thing you’ll notice in Objective-C is the heavy use of brackets. In Objective-C, methods are not called in a traditional sense; instead, they are sent messages. Likewise, a method doesn’t return, but rather responds to the message. Unlike C, where function calls must be predefined, Objective-C’s messaging style allows the developer to dynamically create new methods and messages at runtime. The downside to this is that it’s entirely possible to send an object a message to which it can’t respond, causing an exception and likely program termination.

Given an object named myWidget, a message can be sent to its powerOn method this way:

returnValue = [ myWidget powerOn ];

The C++ equivalent of this might look like:

returnValue = myWidget->powerOn(  );

The C equivalent might declare a function inside of its flat namespace:

returnValue = widget_powerOn(myWidget);

Arguments can also be passed with messages, provided that an object can receive them. The following example invokes a method named setSpeed and passes two arguments:

returnValue = [ myWidget setSpeed: 10.0 withMass: 33.0 ];

Notice the second argument is explicitly named in the message. This allows multiple methods with the same name and data types to be declared—polymorphism on steroids:

returnValue = [ myWidget setSpeed: 10.0 withMass: 33.0 ];
returnValue = [ myWidget setSpeed: 10.0 withGyroscope: 10.0 ];

Class and Method Declarations

While C++ classes can be defined in Objective-C, the whole point of using the language is to take advantage of Objective-C’s own objects and features. This extends to its use of interfaces. In standard C++, classes are structures, and their variables and methods are contained inside the structure. Objective-C, on the other hand, keeps its variables in one part of the class and methods in another. The language also requires that the interface declaration be specifically declared in its own code block (called @interface) separate from the block containing the implementation (called @implementation). The methods themselves are also constructed in a Smalltalk-esque fashion, and look very little like regular C functions.

The interface for our widget example might look like Example 2.1, “Sample interface (MyWidget.h)”, which is a file named MyWidget.h.

Example 2.1. Sample interface (MyWidget.h)

#import <Foundation/Foundation.h>

@interface MyWidget : BaseWidget
{
    BOOL isPoweredOn;
    @private float speed;
    @protected float mass;
    @protected float gyroscope;
}
+ (id)alloc;
- (BOOL)needsBatteries;
- (BOOL)powerOn;
- (void)setSpeed:(float)_speed;
- (void)setSpeed:(float)_speed withMass:(float)_mass;
- (void)setSpeed:(float)_speed withGyroscope:(float)_gyroscope;
@end

Each of the important semantic elements in this file are explained in the following sections.

Imports

The preprocessor directive #import replaces the traditional #include directive (although #include may still be used). One advantage to using #import is that it has built-in logic to ensure that the same resource is never included more than once. This replaces the roundabout use of macro flags found routinely in C code:

#ifndef _MYWIDGET_H
#define _MYWIDGET_H
...
#endif

Interface declaration

The interface is declared with the @interface statement followed by the interface’s name and the base class (if any) it is derived from. The block is ended with the @end statement.

Methods

Methods are declared outside of the braces structure. A plus sign (+) identifies the method as a static method, while a minus sign (−) declares the method as an instance method. Thus, the alloc method (to allocate a new object) will be called using a reference directly to the MyWidget class, whereas methods that are specific to an instance of the MyWidget class, such as needsBatteries and powerOn, will be invoked on the instance returned by alloc.

Every declared argument for a method is represented by a data type, local variable names, and an optional external variable name. Examples of external variable names in Example 2.1, “Sample interface (MyWidget.h)” are withMass and withGyroscope. The notifier (calling function) that invokes the method refers to external variable names, but inside the method the arguments are referenced using their local variable name. Thus, the setSpeed method uses the local _mass variable to retrieve the value passed as withMass.

If no external variable name is supplied in the declaration, the variable is referenced only with a colon, for example, :10.0.

Implementation

The code suffix for Objective-C source is .m. A skeleton implementation of the widget class from the last section might look like Example 2.2, “Sample implementation (MyWidget.m)”, which is named MyWidget.m.

Example 2.2. Sample implementation (MyWidget.m)

#import "MyWidget.h"

@implementation MyWidget

+ (id)alloc {
}

+ (BOOL)needsBatteries {
    return YES;
}

− (BOOL)powerOn {
    isPoweredOn = YES;
    return YES;
}

− (void)setSpeed:(float)_speed {
    speed = _speed;
}

− (void)setSpeed:(float)_speed withMass:(float)_mass {
    speed = _speed;
    mass = _mass;
}

− (void)setSpeed:(float)_speed withGyroscope:(float)_gyroscope {
    speed = _speed;
    gyroscope = _gyroscope;
}
@end

Just as the interface was contained within its own code block, the implementation begins with an @implementation statement and ends with @end. In C++, it is common practice to prefix member variables with m_ so that public methods can accept the name of the variable. This makes it easy to reuse someone else’s code because they can deduce a variable’s purpose by its name. Since Objective-C allows for an external variable name to be used, the method is able to provide a sensible name for the developer to use while internally using some proprietary name. The true name can then be used inside the object, while the method’s local variable name is prefixed with an underscore, e.g., _speed.

Categories

Objective-C adds a new element to object-oriented programming called categories. Categories were designed to solve the problem where base classes are treated as fragile to prevent seemingly innocuous changes from breaking the more complex derived classes. When a program grows to a certain size, the developer can often become afraid to touch the smaller base classes because it’s too difficult by then to determine what changes are safe without auditing the entire application. Categories provide a mechanism to add functionality to smaller classes without affecting other objects.

A category class can be placed “on top” of a smaller class, adding to or replacing methods within the base class. This can be done without recompiling or even having access to the base classes’ source code. Categories allows for base classes to be expanded within a limited scope, so that any objects using the base class (and not the category) will continue to see the original version. From a development perspective, this makes it much easier to improve on a class written by a different developer. At runtime, portions of code using the category will see the new version of the class, and code using the base class directly will see only the original version.

The difference between inheritance and categories is the difference between tricking out your car versus dressing it up as a parade float. When you soup up your sports car, new components are added to the internals of the vehicle that cause it to perform differently. Sometimes components are even pulled out and replaced with new ones. The act of adding a new component to the engine, such as a turbo, affects the function of the entire vehicle. This is how inheritance works.

Categories, on the other hand, are more like a parade float in that the vehicle remains completely intact, but cardboard cutouts and papier-mâché are affixed to the outside of the vehicle so that it appears different. In the context of a parade, the vehicle is a completely different animal, but when you take it to the mechanic, it’s the same old stock car you’ve been driving around.

The widget factory is coming out with a new type of widget that can fly through space, but is concerned that making changes to their base class might break existing applications. By building a category, applications using the MyWidget base class will continue to see the original class, while the newer space applications will use a category instead. The following example builds a new category named MySpaceWidget on top of the existing MyWidget base class. Because we need the ability to blow things up in space, a method named selfDestruct is added. This category also replaces the existing powerOn method with its own. Contrast the use of parentheses here to hold the MySpaceWidget contained class with the use of a colon in Example 2.1, “Sample interface (MyWidget.h)” to carry out inheritance:

#import "MyWidget.h"

@interface MyWidget (MySpaceWidget)
- (void)selfDestruct;
- (BOOL)powerOn;
@end

Example 2.3, “Sample category (MySpaceWidget.m)” shows a complete source file implementing the category.

Example 2.3. Sample category (MySpaceWidget.m)

#import "MySpaceWidget.h"

@implementation MyWidget (MySpaceWidget)

- (void)selfDestruct {
    isPoweredOn = 0;
    speed = 1000.0;
    mass = 0;
}

- (BOOL)powerOn {
    if (speed == 0) {
        isPoweredOn = YES;
        return YES;
    }

    /* Don't power on if the spaceship is moving */
    return NO;
}
@end

Posing

In Objective-C, a subclass class can pose as one of its superclasses, virtually replacing it as the recipient of all messages. This is similar to overriding, only an entire class is being overridden instead of a single method. A posing class is not permitted to declare any new variables, although it may override or replace existing methods. Posing is similar to categories in that it allows a developer to augment an existing class at runtime.

In past examples, mechanical widget classes were created. Well, at some point after designing all of these widgets, perpetual energy was discovered. This allowed many of the newer widgets to be autonomous, while some legacy widgets still required batteries. Because autonomous widgets have such a significant amount of different code, a new object called MyAutonomousWidget was derived to override all of the functionality that has changed, such as the static needsBatteries method. See Examples 2.4 and 2.5.

Example 2.4. Sample interface for posing (MyAutonomousWidget.h)

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

@interface MyAutonomousWidget : MyWidget
{

}

+ (BOOL)needsBatteries;
@end

Example 2.5. Sample implementation for posing (MyAutonomousWidget.m)

#import "MyAutonomousWidget.h"

@implementation MyAutonomousWidget

+ (BOOL)needsBatteries {
    return NO;
}
@end

Instead of changing all of the existing code to use this class, the autonomous class can simply pose as the widget class. The class_poseAs method is called from the main program or another high-level method to invoke this behavior:

MyAutonomousWidget *myAutoWidget = [ MyAutonomousWidget alloc ];
MyWidget *myWidget = [ MyWidget alloc ];
class_poseAs(myAutoWidget, myWidget);

At this point, any other methods we’ve replaced in the posing class (to change how we talk to autonomous devices) would pose as the original base class.

Further Study

To learn more about Objective-C programming, check out the following great resources from O’Reilly:

Learning Cocoa with Objective-C, Second Edition, by James Duncan Davidson

http://www.oreilly.com/catalog/learncocoa2/

Objective-C Pocket Reference by Andrew M. Duncan

http://www.oreilly.com/catalog/objectcpr/


[1] Technically, it’s possible to run an application directly from the iPhone’s command line, but this breaks many application-level functions. SpringBoard itself is heavily integrated with the user interface framework, and your applications will need to be assembled and invoked appropriately to make them entirely usable.

If you enjoyed this excerpt, buy a copy of iPhone Open Application Development.