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.
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++.
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:
The directory that all of the application’s resources reside in.
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.
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.
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.
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.
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.
The first thing you’ll need to do before building any applications 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 web site 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.
As we discussed in Chapter 1, 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.
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). The commands and path names provided throughout this book presume that you’ve used the procedures from this chapter to build and install the tool chain. The tool chain is updated periodically as new versions of it are released, so its setup can sometimes change. The instructions here will guide you through the steps to install version 0.30 of the tool chain, which is the latest available at the time of this writing. Newer versions are documented on the official tool chain project page at http://code.google.com/p/iphone-dev.
The tool chain builds and installs into /usr/local 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 /usr/local out of the way and start with a fresh directory.
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.
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.
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 Gusty Gibbon, Intel
Fedora Core, Intel
Gentoo Linux 2007.0, x86_64
Debian 2.6.18
CentOS 4
Nicholas Penree of Conceited Software (http://www.conceitedsoftware.com) took time to adapt the tool chain’s installation to run on Leopard, which is what we’ll use in our example. Other platforms follow the same basic steps as these. Official tool chain instructions can be found at http://code.google.com/p/iphone-dev/wiki/Building.
The tool chain is several hundred megabytes in size—and that’s 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.
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)
svn (the Subversion source control utility)
If you’re missing any of these tools, download and install them before proceeding. On the Mac, these 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.
XCode tools can be downloaded from Apple’s web site at http://developer.apple.com/tools/xcode/.
Finally, the last thing you’ll need is 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:
Installing the tool chain requires that you copy libraries from your iPhone to your desktop. Check with your local, state, and federal laws to ensure this is legal where you reside.
Having installed SSH onto the iPhone in Chapter 1, use the following commands to download the files you need into a folder called /usr/local/share/iphone-filesystem.
#mkdir -p /usr/local/share/iphone-filesystem
#cd /usr/local/share/iphone-filesystem
#mkdir -p ./System/Library ./usr
#scp -r root@iphone:/System/Library/Frameworks/ .
#mv Frameworks ./System/Library
#scp -r root@iphone:/usr/lib .
#mv lib ./usr
The source code for the tool chain is split into two repositories: one for the LLVM compiler framework and one for the rest of the tool chain. Create a build directory for yourself and cd into it. Now, use Subversion to check both projects out from their repositories.
$svn co
http://llvm.org/svn/llvm-project/llvm/trunkllvm-svn -r 42498
$svn co
http://iphone-dev.googlecode.com/svn/trunk/iphone-dev
The download may take an hour or longer, depending on the speed of your connection. Once both repositories have been checked out, it’s time to start building.
The instructions in the next sections presume you know how to use a terminal window. On the Mac, this can be found in your Utilities folder inside Applications. It is also assumed that you have some level of knowledge of Unix.
One convention that may be unfamiliar to you is the use of the built-in shell commands pushd and popd. These are similar to the cd
command, but they push and pop directories on a stack. This makes it easy to do some work in a new directory and then return to a previous one without having to remember where you were.
You’ll also see references to the sudo command. This is a Unix tool providing limited access to run privileged (root) commands. When you want to run a command that requires privileged access (because it accesses sensitive data on the operating system or could have dangerous effects hurting the operating system), type sudo
on the command line followed by the command you wish to run. Mac OS X will then prompt you for the root password to your desktop before allowing the command to run. If you don’t have sudo, you can safely leave it off of your commands, but you’ll first need to invoke su to become root.
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.
$pushd llvm-svn
$./configure --enable-optimized
$make ENABLE_OPTIMIZED=1
$sudo make install
$LLVMOBJDIR=`pwd`
$popd
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.
$pushd iphone-dev
$sudo mkdir /usr/local/arm-apple-darwin
$mkdir -p build/odcctools
$pushd build/odcctools
$../../odcctools/configure --target=arm-apple-darwin --disable-ld64
$export INCPRIVEXT="-isysroot /Developer/SDKs/MacOSX10.4u.sdk"
$make
$sudo make install
$popd
$HEAVENLY=/usr/local/share/iphone-filesystem
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 patched to work for the iPhone. Issue the following commands to patch the headers (don’t worry, the originals are automatically backed up).
$pushd include
$./configure --with-macosx-sdk=/Developer/SDKs/MacOSX10.4u.sdk
$sudo bash install-headers.sh
$popd
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 build/csu
$pushd build/csu
$../../csu/configure --host=arm-apple-darwin
$sudo make install
$popd
Now that the LLVM framework, cross-compiler tools, and Csu have been built, the compiler itself can now be built and installed. If you’re doing this in stages or have since closed your terminal window, make sure that you’ve still got the environment variables $LLVMOBJDIR
and $HEAVENLY
set to the proper directories. The LLVMOBJDIR
variable points to the location of LLVM object files, which were compiled when you built LLVM. These are used to build llvm-gcc. The HEAVENLY
variable points to the location where you copied the iPhone’s libraries onto your desktop. This directory is used by llvm-gcc to link to the iPhone’s framework and library files when you compile applications. The name “heavenly” was the code name given by Apple to the 1.0 code base of the iPhone software. The 1.1 code base is code-named “snowbird,” but the original name is still used in the tool chain.
$ set | grep -e LLVMOBJDIR -e HEAVENLY
If you don’t see output from the previous command, you need to set the environment variables again. Get back into your build directory and run:
$pushd llvm-svn && LLVMOBJDIR=`pwd` && popd
$HEAVENLY=/usr/local/share/iphone-filesystem
Once you’ve ensured that these are set, issue the following commands to build and install the compiler:
$mv llvm-gcc-4.0-iphone/configure llvm-gcc-4.0-iphone/configure.old
$sed 's/^FLAGS_FOR_TARGET=$/FLAGS_FOR_TARGET=${FLAGS_FOR_TARGET-}/g' llvm-gcc-4.0-iphone/configure.old > llvm-gcc-4.0-iphone/configure
$sudo ln -s /usr/local/arm-apple-darwin/lib/crt1.o /usr/local/arm-apple-darwin/lib/crt1.10.5.o
$mkdir -p build/llvm-gcc-4.0-iphone
$pushd build/llvm-gcc-4.0-iphone
$export FLAGS_FOR_TARGET="-mmacosx-version-min=10.1"
$sh ../../llvm-gcc-4.0-iphone/configure --enable-llvm=`llvm-config --obj-root` --enable-languages=c,c++,objc,obj-c++ --target=arm-apple-darwin --enable-sjlj-exceptions --with-heavenly=$HEAVENLY --with-as=/usr/local/bin/arm-apple-darwin-as --with-ld=/usr/local/bin/arm-apple-darwin-ld
$make LLVM_VERSION_INFO=2.0-svn-iphone-dev-0.3-svn
$sudo make install
$popd
$popd
Congratulations! You’ve built the free tool chain for iPhone. You’re now ready to start compiling iPhone applications. The compiler itself can be invoked directly by calling /usr/local/bin/arm-apple-darwin-gcc. We’ll explain how to use it in the next section.
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 /usr/local/bin is in your path before you try to use the cross-compiler.
$ export PATH=$PATH:/usr/local/bin
The anatomy of a typical command line compile is:
$ arm-apple-darwin-gcc -o MyExample MyExample.m -lobjc
-framework CoreFoundation -framework Foundation
arm-apple-darwin-gcc
The name of the cross-compiler itself. This is located in /usr/local/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.
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 = /usr/local/bin/arm-apple-darwin-gcc LD = $(CC) LDFLAGS = -lobjc -framework CoreFoundation CFLAGS = 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 $@
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
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, 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. Pressing the icon will launch it.
While most developers use the command line or a makefile to build iPhone applications, the tool chain can be partially integrated with XCode to take advantage of the feature-rich editor and one-click build process. XCode treats the tool chain as an external resource and calls it to perform the actual build.
To integrate with XCode, a template is required to glue the necessary pieces to the tool chain. John Robinson from Monster and Friends has designed an XCode template based on one written by tool chain developer Lucas Newman. We’ve made some changes to tailor it to this book. Our version can be found in the download section for this book at http://www.oreilly.com/catalog/9780596518554/. John’s original version can be found at http://www.monsterandfriends.com/?q=node/62.
To integrate the cool chain with XCode, perform the following steps:
Download and install XCode version 3.0 or later from Apple’s developer web site.
Download our version of the template from the O’Reilly web site and extract the archive’s contents.
A folder named iPhone UIKit Application will be created. Copy this directory into /Developer/Library/Xcode/Project Templates/Application on your Mac.
Once you’ve installed the template, you can now create a new iPhone project by performing these steps:
Launch XCode and select New Project from the File menu. A list of templates will be presented to you to choose from.
In the Application section of the list, scroll to the iPhone UIKit Application template and create a new project using it as the template.
A new project will now be created, and the project name will be reflected in the filenames used. To build the application, just click the Build
button on the toolbar. XCode will call the tool chain’s compiler and place a binary in the project’s build folder.
Because XCode treats the tool chain as an external resource, it’s not completely integrated with it. What happens under the hood is this: XCode kicks off a build process using a makefile present in the project template. This makefile calls the tool chain’s compiler with the appropriate options. To add new files or frameworks to your project, you’ll need to manually edit the file named Makefile shown in your project’s file list. This will bear a resemblance to the one discussed in the last section.
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 carried 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.
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 ];
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, which is a file named 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.
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 round-about use of macro flags found routinely in C code:
#ifndef _MYWIDGET_H #define _MYWIDGET_H ... #endif
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 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 name, and an optional external variable name. Examples of external variable names in Example 2-1 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 name is supplied in the declaration, the variable is referenced only with a colon, for example, :10.0
.
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, which is named 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
.
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 categories and inheritance 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 to carry out inheritance.
#import "MyWidget.h" @interface MyWidget (MySpaceWidget) - (void)selfDestruct; - (BOOL)powerOn; @end
Example 2-3 shows a complete source file implementing the category.
#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
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 Example 2-4 and Example 2-5.
#import <Foundation/Foundation.h> #import "MyWidget.h" @interface MyAutonomousWidget : MyWidget { } + (BOOL)needsBatteries; @end
#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.
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 (Apple Computers, Inc.)
http://www.oreilly.com/catalog/learncocoa2/ |
Objective-C Pocket Reference by Andrew M. Duncan (O’Reilly)
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.