O'Reilly logo

Learning the iOS 4 SDK for JavaScript Programmers by Danny Goodman

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Structural Overview of an iOS App

Now that we have a working iOS app, we can look more closely at many of the details in the source code for that app and the project. Xcode will again be your gateway to the inner workings. Although the title of this chapter doesn’t mention object-oriented programming (OOP), this chapter is where you will learn the fundamentals—especially the terminology, which is often quite different from what you’ve seen in the JavaScript world. Despite the differences in naming, you will recognize some familiar concepts.

Where It All Begins: APIs

An application programming interface, or API, is essentially a list of entities you can program and what you can ask those entities to do in a program. Designers of computers and software systems can choose to expose the inner workings of those devices and programs to third-party programmers. APIs typically shield the third-party programmer from the complex inner workings of the systems and provide him with simpler, optimized, and well-tested ways to control those systems.

APIs You Already Know

As a client-side scripter, you have already worked with a number of APIs, even if the three-letter term wasn’t forced upon you. The API most obvious to you should be the DOM, which exposes every type of HTML element as an object you can script. The authors of the W3C DOM write specifications about how objects are named, what their properties and methods are, and how objects are to behave under script control. The initial audience for these standards is browser makers who implement the standards and provide the APIs that you access via JavaScript.

APIs for current programming environments rarely stand still. That has certainly been the case in the browser API world. The evolving HTML5 standard includes an API for new HTML elements that facilitate embedding audio and video into a web page, as well as other APIs for offline web applications and dynamically editable content. A browser maker can also elect to add APIs, which is what Apple has done with Mobile Safari to enable limited access to iOS location services through a web page’s scripts.

The JavaScript language provides its own APIs for the core language. The way your scripts work with strings, numbers, dates, regular expressions, and operations through the Math object are all part of that API. All of those properties and methods you see in reference guides are there to provide you with easy access to what would otherwise be very complex, low-level programming.

The Cocoa Touch APIs

To allow developers to write apps for the iPhone and similar devices, Apple’s engineers created a set of APIs. The historical basis for the iOS APIs was the set of Mac OS X development APIs. On the Mac, the most recent set of APIs is known as Cocoa (whose technological heritage, in turn, originated with the NeXTSTEP development environment). Although the name Cocoa was originally associated with a children’s multimedia creation environment (by Apple), the name eventually shifted to the Mac OS X development world, where it has remained.

Because so many fundamentals of iOS are based on Mac OS X but implemented for purely touch-based devices, it was logical enough to call the iOS APIs Cocoa Touch. That’s not to say Cocoa Touch is merely Cocoa with the addition of some touchscreen events. Cocoa Touch omits many Cocoa technologies that aren’t necessary for a slimmed-down device (compared to a desktop or laptop computer) and adds gobs of technologies that Mac OS X computers may never acquire—you’re not likely to rotate your MacBook display to portrait mode or demand turn-by-turn directions from an iMac.

Cocoa Touch APIs consist of specifications for objects and built-in functions that programmers can use while developing apps for iOS devices. Just as the JavaScript API in browsers supplies scriptable access to strings, numbers, and dates, the Cocoa Touch API provides Objective-C access to those same kinds of data. And, just as the DOM API in browsers specifies how JavaScript can access and control document objects, such as forms, arbitrary divs, or images, the Cocoa Touch API lets programmers access and control buttons, images, and scrollable regions within a view.

But of course, the Cocoa Touch APIs grant lots of OS-specific access to features such as the device’s geographical location, the music library in the built-in iPod app, and those ubiquitous scrolling lists of items. With each new generation of iOS, Apple adds more APIs. For example, at the announcement of the iOS 4.0, Apple claimed to have added over 1,500 methods to the SDK, including access to a user’s calendar data and files in the photo library and the ability to build SMS messaging into apps. For iOS 4.2, Apple added scores of APIs, including a MIDI controller and wireless printing facilities.

The job of an iOS SDK programmer, therefore, is to use the Objective-C language to manipulate data (text, graphics, sounds, etc.) with the help of the APIs built into the operating system. You’ll still need good ideas about the data and how best to integrate that data into the device, but learning the broad spectrum of APIs available to you is a very important part of the learning process.

Frameworks

One way Apple keeps the thousands of APIs straight is to organize them into groups known as frameworks. You may recognize the term from your exposure to JavaScript libraries such as Dojo, jQuery, and YUI. Although there are differences between libraries and frameworks (a subject beyond the scope of this book, and usually generating programming-religious discussions), they both provide APIs that facilitate programming-specific tasks. Frameworks targeting iOS web app development include iUI and a jQuery plug-in called jQTouch. The iUI framework consists of CSS and JavaScript files you link into a web page. The JavaScript objects and functions in the .js file are immediately available within the scope of the current document window. For example, the iUI framework performs much of the heavy lifting in turning an HTML unordered list into a page that resembles the typical iPhone vertically scrollable table view; a tap on an item (via a touch event) causes what looks like a detail view to slide in from the right side.

You can see the three basic frameworks that are common to virtually every iOS app created with the SDK. Open the Workbench project in Xcode and expand the Frameworks folder in the Files and Groups panel on the left. There you will see entries for the Foundation, UIKit, and CoreGraphics frameworks (Figure 4-1).

Three basic iOS SDK frameworks
Figure 4-1. Three basic iOS SDK frameworks

You don’t truly have to know what’s inside each of these three frameworks, but having an overview of them from 35,000 feet will help you understand how the iOS SDK organizes its various powers. You are welcome to expand any of the framework items, but the long lists of header files might make your head explode until you progress further into this chapter. With that, let’s do a quick framework survey.

Foundation Framework

The Foundation framework reveals how much iOS has in common with Mac OS X. Both environments use the same Foundation framework, although the iOS version trims off many items that don’t apply or aren’t yet supported. Definitions in this framework include the basics: strings, various types of data collections, dates, timers, fetching data from the network through URLs, and even XML parsing (although not in a DOM-friendly manner). The Foundation framework is all about manipulating data, and not displaying it. In that sense, you might equate this framework with the core JavaScript language, which is designed for use in all kinds of environments, including those outside of web browsers.

UIKit Framework

As its name implies, the UIKit framework is all about user interface elements—specifically, iOS kinds of user interface elements. This is where the SDK lets you create and manipulate buttons, images, scrolling thingies, text boxes, various bars, alert dialog windows, and those neat looking slot-machine-style picker controls. Most of the time you will use Interface Builder to lay out user interface elements for a screen, but under the hood, the SDK ultimately relies on the UIKit framework definitions for those elements to figure out how to draw them when the app runs. If the Foundation framework is like the core JavaScript language API, the UIKit framework is like the DOM API, including the JavaScript style property to control very specifically how things on the screen appear (colors, sizes, etc.).

CoreGraphics Framework

Unlike the Objective-C-based Foundation and UIKit frameworks, the CoreGraphics framework uses the C language in all of its definitions. This is one reason you need to be familiar with basic C syntax when doing iOS SDK programming. The CoreGraphics framework focuses on 2-D drawing activities, such as when you need to fill a space with a gradient or rotate a user interface control (e.g., creating a vertical slider control).

Adding Frameworks

The above descriptions are brief, to be sure, but you’ll recognize that I make no mention of some of the specialized capabilities of iOS apps, such as location services, maps, or playing audio. That’s because APIs for those capabilities are contained in other frameworks that are not part of the default set that Xcode automatically puts into a view-based application. If you want to get an idea of the other frameworks available to you, Control-click the Frameworks group and follow the submenus to Add→Existing Frameworks, as shown in Figure 4-2.

Control-click the Frameworks group
Figure 4-2. Control-click the Frameworks group

In the resulting dialog box, choose Frameworks from the top menu to limit the list to available frameworks, as shown in Figure 4-3. You can see the range of subject matter covered by Apple’s documented frameworks. Don’t add anything to the Workbench project now. In Appendix A, you’ll learn how to use the SDK documentation to determine which framework to add to your project for a particular feature you want to add to your app.

List of existing frameworks
Figure 4-3. List of existing frameworks

Frameworks Set in Stone

You may be accustomed to inspecting and even modifying the source code in JavaScript libraries that come in various web browser scripting frameworks. That’s one significant difference between a JavaScript library and an iOS SDK framework. The source code that does the work of the iOS APIs is hidden away. At best, you can see lists of the API calls available within a framework, but not how those frameworks actually do their work. That also means that you cannot alter the frameworks directly. That’s a good thing: given the complexity of iOS, such willy-nilly modifications could spell trouble for your app, because you never know when your code or another API relies on a mint-condition API call. Instead, Objective-C provides you with multiple ways to add to the existing functionality of an API that your program can use at will. (See Chapter 8).

Now that you’ve explored the Frameworks group in Xcode, it’s time to head back up to the Class Files group in the Workbench project. This is where you will spend most of your coding time.

Welcome to Class Files

Most, if not all, of the code you write for an iOS app is contained in what are called class files. A class is an object-oriented programming term and is vital to understanding most of what goes on in iOS SDK programming.

A class is code that defines the characteristics of an object. You’ve worked with tons of objects in JavaScript—whether they be pure language objects, such as the Date object, or DOM objects that represent HTML elements on the page—so you should already be familiar with the concept of an object having properties (which can hold data) and methods (which perform actions). In fact, when you write the code for a JavaScript object constructor function, you are doing essentially the same job that you will be doing when you write the code for an Objective-C class definition. It’s just that the format and environment for the Objective-C variety is radically different from the JavaScript constructor. To help you visualize the differences between the JavaScript object constructor and Objective-C class definitions, I’ll show you a variation on a venerable JavaScript object constructor example, which I will then rewrite in Objective-C (you can even test it in Workbench by the end of the chapter). Both examples focus on using the constructor or class as a way to obtain object instances.

The JavaScript Way

I’ll begin with a variation of an “old chestnut” example, one that defines an object that represents an automobile. I’ll add a function to the old standard to demonstrate how an object gains a method. Example 4-1 is an HTML page with scripts that define a Car object constructor, create two instances of that object, and then assemble a text description out of properties of each object instance. The results are inserted into span elements on the page.

Example 4-1. JavaScript object constructor at work
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Car Object Example</title>
<script type="text/javascript">
// Constructor function
function Car(make, model, year) {
   this.make = make;
   this.model = model;
   this.year = year;
   this.getFormattedListing = function() {
       var result = "[" + this.year + "] " + this.make +
       " " + this.model;
       return result;
   }
}
// Create instances of Car
var myCar1 = new Car("Chevrolet", "Malibu", "2007");
var myCar2 = new Car("Ford", "Taurus", "2010")

// Insert Car data in display elements
function showCar() {
   document.getElementById("display1").innerHTML = myCar1.getFormattedListing();
   document.getElementById("display2").innerHTML = myCar2.getFormattedListing();
}
</script>
</head>

<body onload="showCar()">
<h1>Info About My First Car</h1>
<p>I used to drive a <span id="display1"></span>.</p>
<h1>Info About My Current Car</h1>
<p>I now drive a <span id="display2"></span>.</p>
</body>
</html>

After the page loads, the showCar() function fills in content of two span elements with formatted text descriptions of the two cars. The constructor function is reused, as-is, to create two instances of the Car object, each with its own details, but the function always formats the results in the same way.

The Objective-C Way

I’ll begin by showing you the code for a class definition that will let you generate instances of objects representing car information. Much of what you’ll see at first will likely be foreign to you, but don’t fear. I will dissect and explain the code to fill in the blanks. Don’t go rushing to recreate these files just yet; Xcode can do a lot of the grunt work for you, as I’ll demonstrate later in this chapter.

An Objective-C class is described using two different sections of code, called the interface and the implementation. Although you can combine the two sections into a single file, the convention that Xcode follows when it generates templates for classes is to place each of these sections into its own file. The filenames are the same, but each file has its own filename extension to differentiate the two.

The interface contains a list of variables and methods that the class wishes to expose to other classes. Items are defined with just the barest minimum of detail, such as the name and type of each variable or the name of a method, which kind of data it returns, and which arguments are required. In contrast, the implementation section contains the code that executes when each method is invoked.

Example 4-2 shows the interface portion of a definition for the DGCar class. Notice that the class name (DGCar) begins with an uppercase letter—another Objective-C convention (but not an absolute requirement). Interface files always have the .h filename extension. The “h” comes from the word “header,” and I will refer to .h files as header files. I could have named the class Car, but that is such a generic term that it could conceivably be used by the frameworks supplied in the system I’m programming (e.g., if Apple ever got into the automobile business). To greatly reduce possible confusion—since each class must have a unique name inside a project—I put my initials as the first two letters of the class name. You’ll find that Apple does this all over its frameworks. Every class name in the Foundation framework begins with “NS” (revealing its NeXTSTEP heritage) and every class name in the UIKit framework begins with “UI.” Other frameworks define their class names with still other prefixes. Pick a class naming prefix, and stick with it so you can always identify your custom classes and keep them separate from Apple’s framework classes or other third-party classes you adopt in the future.

Example 4-2. The header file for the DGCar class—DGCar.h
//
//  DGCar.h
//  Workbench
//
//  Created by Danny Goodman on 4/12/10.
//  Copyright 2010 Danny Goodman. All rights reserved.
//

#import <Foundation/Foundation.h>


@interface DGCar : NSObject {

    NSString *make;
    NSString *model;
    NSString *year;

}

- (DGCar *)initWithCarMake:(NSString *)inputMake
                     model:(NSString *)inputModel
                      year:(NSString *)inputYear;
- (NSString *)getFormattedListing;

@end

Example 4-3 shows the implementation file for the DGCar class. The implementation file’s extension is .m, where the “m” originates from “methods”—not, as some suggest, ’mplementation. Although the DGCar class methods file contains two method definitions, the file can also contain variables, constants, and other components that the methods use. I will refer to .m files as implementation files. You’ll learn about method formatting (e.g., the leading hyphen and colons) later in this chapter when I show you how to create and edit these files.

Example 4-3. The implementation file for the DGCar class—DGCar.m
//
//  DGCar.m
//  Workbench
//
//  Created by Danny Goodman on 4/12/10.
//  Copyright 2010 Danny Goodman. All rights reserved.
//

#import "DGCar.h"


@implementation DGCar

- (DGCar *)initWithCarMake:(NSString *)inputMake
                     model:(NSString *)inputModel
                      year:(NSString *)inputYear {
    // If initialization of the superclass succeeds,
    // then assign instance variables
    if (self = [super init]) {
        make = inputMake;
        model = inputModel;
        year = inputYear;
    }

    return self;
}

- (NSString *)getFormattedListing {
    NSString *result = @"";

    result = [NSString stringWithFormat:@"[%@] %@ %@", year, make, model];

    return result;
}
@end

The header and implementation files work together, and their code must be kept in sync. Coming from the JavaScript world where “once is enough,” you will see what appears to be duplication in the content of the header and implementation files in Objective-C. Compilers rely on the concise header section to confirm the availability of variables and methods referenced by other classes. If you have worked exclusively with interpreted languages, it may take some time to remember to make changes to both sections whenever you edit a class. If you do forget, Xcode will remind you when you attempt to build the app.

Notice that the two Objective-C DGCar class files correspond to the role of only the JavaScript Car constructor function. Instances of the objects (in both cases) are created elsewhere.

Note

Experienced Objective-C coders may notice the absence of memory management in the code examples for this chapter. Fear not! Chapter 6 covers the subject, and eventually this code will be outfitted with proper memory management routines.

Header File Details

When Xcode generates a class file, it starts by inserting several comment lines at the very top:

//
//  DGCar.h
//  Workbench
//
//  Created by Danny Goodman on 4/12/10.
//  Copyright 2010 Danny Goodman. All rights reserved.
//

It should be obvious that Xcode is picking up various pieces of information from the file and project names as well as the computer user’s name. Xcode inserts those tidbits automatically. If the copyright line of the comments in files you create looks like the following, you need to show Xcode or the specific project how to fill in the name of your organization:

//  Copyright 2010 __MyCompanyName__. All rights reserved.

One way is to make sure that your contact card in Apple’s Address Book app on your Mac has a company name filled in (even if it is a duplicate of your own name). Xcode looks there for the organization by default. Or you can set the organization name separately for a specific project by opening your project in Xcode, choosing Project→Edit Project Settings, and filling in the Organization Name field near the bottom of the General tab. Changing this setting does not alter copyright attribution on source code files already generated in the project. You might want a separate organization name if you are developing an app for someone else or your contact card lists an employer’s company while you develop apps on your own.

After Xcode generates these comment lines, you are free to delete or edit them as you like. The compiler ignores all commented lines when building an app.

Importing frameworks

At the top of the uncommented code in the header file comes the place where you declare which framework(s) the current class requires:

#import <Foundation/Foundation.h>

Statements beginning with the pound symbol (#) are called preprocessor directives. When you build (compile) an app with Xcode, source code files are initially scanned by a preprocessor, which obeys instructions in these directives. There are a limited number of preprocessor directives, but #import is one you will use frequently. It instructs the compiler to locate the file and blend it into the current file. In some ways, it works like the HTML link element, which commonly brings an external file (e.g., a .css file) into the scope of the current document.

An #import directive connects your program to an associated header file that has the definitions you need. The directive has two ways of referring to files that are to be imported into the class. The one shown above, with the angle brackets, typically imports standard frameworks. The other style (which you’ll see in the DGCar methods file shortly) is for importing class files you bundle in your own app.

In the case of DGCar.h, the Foundation framework (which you can visualize as a folder) contains a bunch of header files, one of which is named Foundation.h. That header file itself contains a long list of #import directives that instruct the preprocessor to import the individual Foundation framework header files (such as NSArray.h and NSDate.h). You can see this for yourself in Xcode for the Workbench project by expanding the Frameworks group folder and then the Foundation framework “toolbox” icon and the nested Headers folder. Click on the Foundation.h file and you’ll see the list of individual framework header files being imported into the DGCar class (Figure 4-4).

A class header can import multiple frameworks. For example, if the purpose of Workbench was to display and work with maps, any class file that needs to communicate with the APIs in the MapKit framework would include an #import directive for that framework:

#import <MapKit/MapKit.h>

You would also have to add the MapKit framework to the project, as discussed earlier in this chapter in Adding Frameworks.

Inspecting the Foundation.h header file
Figure 4-4. Inspecting the Foundation.h header file

Inheritance

Inheritance simply means that a newly defined class automatically gets all of the characteristics and behaviors of an existing class, and adds one or more items that make the new class something different. The new class is called a subclass, while the existing class from which the subclass gets its fundamentals is its superclass. The “child” and “parent” conceptual relationship also fits, but those terms aren’t prevalent in the Objective-C language.

Before you get all tangled up in the notion of inheritance, bear in mind that DOM scripting has already warmed you up to the concept. If you were to examine the W3C standards document for the DOM, you would see that the DOM’s structure is entirely object-oriented underneath, relying on relationships between superclasses and subclasses. For example, the most basic object in the DOM structure is the node. Every instance of a Node object has a list of attributes (more commonly called properties when being scripted) and methods that are defined for the Node. Attributes for a node include nodeName, nodeValue, nodeType, firstChild, and so on; methods include appendChild(), removeChild(), cloneNode(), and others. A node in its pure form is not specified in an HTML document or rendered in a browser: it is merely the basic building block used to define more concrete “things” in a document.

That brings us to the DOM Element object, which inherits directly from the Node object. Thus, an Element object offers the same attributes and methods of a node, plus special members that apply to any kind of element, such as the getAttribute(), setAttribute(), and getElementsByTagName() methods. All of these are defined in the DOM Core module.

In the DOM module dedicated to HTML come definitions for objects more closely related to the items you put into HTML documents. One of the building blocks is the HTMLElement. It inherits all members of the Element and (in turn) Node objects, plus it acquires additional attributes that apply to all HTML elements, such as the id, title, and className properties. Going one step further, take the HTMLImage element (represented in HTML by the <img> tag). It inherits everything from the HTMLElement and adds further attributes that apply to its purpose of loading and displaying images—properties such as src, height, width, and alt. It is only by virtue of the inheritance chain—from HTMLImageElement upward through HTMLElement, Element, and Node—that an HTMLImage element has a nodeName property.

The @interface compiler directive

The identical object-oriented notion of inheritance works in Objective-C and many Cocoa Touch classes. When you name a class of your own creation (remember the initial capital letter convention!), you also specify from what other class your new class inherits in the following format:

@interface NewSubclass : SuperClass

Statements beginning with the @ symbol are called compiler directives. In other words, they are commands to the compiler as it processes the file. The @interface directive tells the compiler that this section contains declarations of the variables and methods that will be fleshed out in the companion @implementation section. The compiler needs to know the name of the class: DGCar in the example here. But to be an Objective-C class that will also be operating with Cocoa Touch classes (like arrays and strings), a custom class needs far more infrastructure than what you’ll be defining in the class files. This infrastructure comes from inheriting all the characteristics of an existing object. Because the DGCar class is a very simple class that needs just the bare essentials, it will inherit from the most fundamental class in the Foundation framework: NSObject. As you will glean over time, most of the objects you work with from the Foundation, UIKit, and other frameworks inherit from NSObject, which assumes the role as the base class.

When you read instructions by other programmers that tell you to create a subclass of some other class, the @interface section begins just as above, with the name of the class you’re creating, a colon, and the name of the superclass. The superclass, in turn, must belong to the framework (or other header file) imported via the #import directive earlier in the header file.

Using Xcode to Create DGCar Class Files

Xcode is helpful in setting up the skeletal structure of your class files, including the inheritance tree. Now is the time to follow along and create the DGCar class files (and fill them in as the chapter progresses).

Begin by selecting the Classes folder in the Files and Groups panel in the Workbench project window. Selecting this folder as a starting point causes Xcode to insert the files you’re about to create within the Class group. To see the next menu, you have two options:

  • Click to select the Classes folder and pull down the Action menu (the one with the gear icon).

  • Use your choice of secondary click (e.g., Ctrl-click, right-click with a two-button mouse, or two-finger-click with a trackpad) on the Classes folder to reveal the context-sensitive menu.

Choose Add to view the choices of new items you can create. Select New File, as shown in Figure 4-5.

The Add submenu
Figure 4-5. The Add submenu

A New File window opens, shown in Figure 4-6. For the simple class being created here, choose Cocoa Touch Class in the left column, then Objective-C class in the top right group. Next, make sure you specify that this will be a subclass of NSObject. This will cause Xcode to fill in as many pieces as it can into the skeleton files, including the superclass name. Click the Next button.

Choices in the New File dialog window
Figure 4-6. Choices in the New File dialog window

The final step in the creation of the set of partially prewired class files is to name the class, shown in Figure 4-7. It is here where you assign a name to the class. The default file name includes the correct .m extension. The name you enter into the top field becomes not only the name of the two class files, but also the name of the class, which Xcode inserts into the class file templates. Xcode fills in everything else in this dialog box, but be sure to enable the checkbox next to the instruction to also create the .h header file for the class. Then click the Finish button.

Saving the new class files
Figure 4-7. Saving the new class files

Et voilà, your two class files are now created and inserted into the Class group, as shown in Figure 4-8. The new header file (below the comments) consists of the following code:

#import <Foundation/Foundation.h>


@interface DGCar : NSObject {

}

@end
The two class files are created and added to the Classes group
Figure 4-8. The two class files are created and added to the Classes group

Editing the @interface Section

Notice that the @interface directive is balanced by an @end directive, which is always at the end of the header file. In between are two sections for your code. The sections are labeled with placeholders in the following code fragment:

#import <Foundation/Foundation.h>


@interface DGCar : NSObject {

    // instance variable declarations here

}

// method declarations here

@end

These two sections are for declarations—almost a table of contents for the variable and method names to be assigned values and defined in detail within the companion implementation file.

For purposes of this demonstration, the DGCar class is designed to be parallel to the JavaScript Car object described earlier in this chapter. The JavaScript object has three properties and a method; the Objective-C version has three instance variables (also called ivars, pronounced EYE-varz) and two method definitions, as follows:

@interface DGCar : NSObject {

    NSString *make;
    NSString *model;
    NSString *year;

}

- (DGCar *)initWithCarMake:(NSString *)inputMake
                     model:(NSString *)inputModel
                     year:(NSString *)inputYear;
- (NSString *)getFormattedListing;

@end

You can probably recognize that the three JavaScript properties and three Objective-C instance variables serve similar purposes: when an instance of the object is created, that instance is designed to hold three values. Importantly, each instance can hold entirely different values without interfering with another—the essence of the object-oriented programming term, encapsulation.

There are, however, some important differences between the two environments. First, you’ll notice that the Objective-C declarations define each variable as belonging to the NSString class. That is to say, each variable (when eventually initialized) will itself be an instance of the NSString class. Unlike JavaScript, when you define an Objective-C variable as a particular type of data, the data must be of that type and cannot change over the life of that variable. You’ll learn more about data typing in Chapter 6.

An asterisk symbol indicates that the variable is what is known as a pointer to a place in memory where the object containing the value lives. The pointer notation appears with a variable when the variable is being declared, as happens in the header file. Chapter 6 has more information about pointers in Objective-C and how to use them.

Of the two methods defined in the DGCar class header file, the second one, getFormattedListing, parallels the like-named method in the JavaScript version. The leading hyphen tells the compiler that the method is an instance method. A method declaration ends with a semicolon.

In Objective-C, all method declarations must specify the type of data returned when the method finishes running. The data type is placed inside parentheses ahead of the method name. The getFormattedListing method returns a value that is of class NSString (more explicitly, a pointer to an NSString object instance). As you’ll learn later, not all methods return a value (just as not all JavaScript methods or functions return values). In those cases, place the keyword void in the parentheses where the data type goes.

Look now at the other method. Based on its name, it certainly appears to have something to do with initialization. The original JavaScript object constructor function accepts three arguments whose values are assigned to object properties at the same time an instance of the object is created. That same operation takes place in the Objective-C initialization method for this class. In Objective-C syntax, you can construct a method definition to accept arguments. Each argument must have its data type declared (again, covered in more detail in Chapter 6) and the name of a variable to which the value is assigned when the method is called (just like the parameter variables in the JavaScript constructor function’s parentheses). The first argument comes after the method name, separated from it by a colon. Subsequent arguments (if any) have individual, explicit labels, which must have a space or other whitespace character (such as a carriage return) between the end of the previous argument’s parameter variable and the start of the label. Thus, the second argument of the initialization method is named model and its value must be of type NSString. The way you refer to a method when writing or speaking about it is to include all of the argument labels and colons (if any). Therefore, the initialization method is technically referred to as:

initWithCarMake:model:year:

One reason the argument labels are so important is that, unlike in JavaScript, you can reuse a method’s name to define a completely different method with a different set of arguments. Therefore, in this same DGCar class, you could define an additional method as the following and there would be no confusion:

initWithCarMake:model:year:listPrice:

The number of arguments and their labels in a statement that calls the method dictate the precise method being called.

Message Passing

While I’m on the subject of calling methods, Objective-C triggers the execution of methods based on a model that is different from what you use in JavaScript. Because each Objective-C object instance is conceptually a separate entity, the way objects communicate with each other is to send messages to each other. A message may have the feel of invoking an object’s method, but there are important differences. For instance, if an object doesn’t understand a message, the message is ignored and the object simply returns nil to the sender. Therefore, get in the habit of using the “message” terminology, such as, “Object A sends the myMethodWithArgument1:andArgument2: message to Object B.”

One advantage of the argument labels in method definitions is that it’s much easier to understand what the values being passed mean. In the JavaScript version, you just pass three strings and hope you have them in the desired order as needed by the order of the parameter variables in the constructor function. In Objective-C, however, the labels guide you to precisely how you should use each value. Importantly, the order of the labels must be the same in the method definition and the statement sending the message to that method. Just because they have labels doesn’t mean you can mix up the order at will.

Speaking of labels, don’t be afraid to be verbose in devising label names that really define what they’re for. In fact, that goes for everything in Objective-C for which you need to assign a name. In JavaScript and web coding, you are very mindful that each character you apply to a variable or method name means a character that must make the journey from server to browser, occupying Internet bandwidth. But in the compiled Objective-C world, the compiler reduces those long names to very compact tokens. Long names in Objective-C contribute to the readability of your code (but you should not rely on them as replacements for good commenting).

Editing the @implementation Section

The implementation file, which holds the @implementation section, is where you write the code that executes when an instance of the class is created and thereafter. Look first at the empty DGCar.m file that Xcode generated for you (omitting the top comments section):

#import "DGCar.h"


@implementation DGCar

@end

Xcode automatically fills in two items. The first is the #import preprocessor directive. Whenever a class definition is divided into separate header and implementation files, the implementation file must instruct the preprocessor to import the companion header file. Notice that for importing class header files that are not frameworks, the header file name is in quotes rather than the angle brackets used for importing frameworks (as was done in the header file). The difference between quotes and brackets is that quotes instruct the preprocessor to start looking for the file within the same directory as the current implementation file. Since framework files are stored elsewhere, the angle brackets tell the preprocessor to skip the current directory.

If the @interface and @implementation sections were in the same file, both sections would already be together, meaning the @implementation section could see all the declarations in the @interface section and no importing would be necessary. Despite the apparent simplicity of having both sections in one file (especially if you need to make changes to method names or arguments during development), don’t fight Xcode’s will in generating header/implementation file pairs for your classes.

The blank @implementation section Xcode generates is even simpler than the blank @interface section shown earlier. There are no curly-braced subsections to deal with. Almost all of your code goes between the @implementation and @end compiler directives.

Adding the getFormattedListing method

Now we’ll inspect the details of a method definition and see how the Objective-C form compares to what you know about JavaScript methods. The two versions described in this chapter share the same basic name, getFormattedListing. The Objective-C version is as follows:

- (NSString *)getFormattedListing {
    NSString *result = @"";

    result = [NSString stringWithFormat:@"[%@] %@ %@", year, make, model];

    return result;
}

Many things about this definition will look familiar to you. Compare the two language formats for method definition structure:

JavaScript:

function methodName() {
    // Statements
}

Objective-C:

- (returnType)methodName {
    // Statements
}

An Objective-C instance method definition begins with a hyphen symbol, followed by the return value type in parentheses and the method name. A block of statements to execute when the object receives a message matching the method name is contained inside a set of curly braces. Different programmers (in JavaScript, too) adhere to different styles of curly brace placement for blocks. Some place the left curly brace on the same line as the method name; others place the left brace at the start of the next line. Use the style you prefer.

The job of the getFormattedListing method is to read the values of the instance variables, insert them into a string, and return the entire string as an NSString type. It begins by creating an empty NSString object named result. The second statement sends a message to the NSString class (not an instance) to invoke one of its class methods to plug the ivar values into placeholders within a string (more about formats in a moment). This NSString message returns a new NSString object, which is assigned to result, the value returned at the end.

Message syntax

You can see in the second statement of getFormattedListing what an Objective-C message looks like (the expression to the right of the assignment (=) operator). Forget everything you know about JavaScript method-calling syntax, and get ready for the Objective-C way of doing things. Sending a message requires at least two pieces of information:

  • A reference to the intended recipient object, i.e., the message’s receiver

  • The name of method to be called (plus any arguments and passed values)

These pieces are embedded inside square brackets in the following format:

[receiver method]

To send a message containing a single argument, the syntax format is as follows:

[receiver method:argument]

Each additional argument consists of a label and a value, separated by a colon, as in the following format:

[receiver method:argument labelA:argumentA labelB:argumentB]

You can add one or more spaces around the square brackets if it helps with your code readability. I predict that until you get used to the square bracket notation, you will inevitably start writing messages without them because of your experience with JavaScript syntax. Additionally, every message includes a receiver, even if the message you’re sending aims to invoke a method in the current class definition. In those cases (as you will see many times later in the book), the receiver is—you guessed it—self, as in the following message, which calls a loadNewData method defined elsewhere in the current class (and which does not return a value):

[self loadNewData];

Adding the initWithCarMake:model:year: method

Every Objective-C object derived from NSObject inherits from that base object the init method. That method is used as part of the process to generate an instance of the object (in addition to memory allocation, which you’ll learn about in Chapter 6). If your custom class does nothing special when it creates an instance of itself, there is no need for you to define an init method in your class: the object will receive the init message and automatically use the init method defined for its superclass, NSObject. That’s how inheritance and message passing work.

In the case of the DGCar class defined here, its code follows the model in the JavaScript version, in that whenever an object instance is created, that instance is handed three string values to be assigned to instance variables. To accommodate that, the DGCar class defines a customized initialization method, named initWithCarMake:model:year:. The definition of that method follows:

- (DGCar *)initWithCarMake:(NSString *)inputMake
                     model:(NSString *)inputModel
                      year:(NSString *)inputYear {
    // If initialization of the superclass succeeds,
    // then assign instance variables
    if (self = [super init]) {
        make = inputMake;
        model = inputModel;
        year = inputYear;
    }
    return self;
}

The method definition signifies with the leading hyphen that it is an instance method. Initialization methods always return a value that is a reference to the object instance. You may have seen this same concept in JavaScript constructor functions that end with a return this statement: the constructor function returns a reference to the instance object just created. Therefore, the initialization method for DGCar returns a reference to the very same object instance being created. The asterisk indicates that the value is a pointer to the object instance in memory (pointers are covered in Chapter 6).

When an Objective-C method accepts arguments, each argument is assigned to a variable—the same way JavaScript function arguments are assigned to parameter variables. The difference in Objective-C is that you must specify the data type for each one. Therefore, the car make must be passed to the DGCar initialization method as an NSString type of object. The same goes for the other two arguments. Just as the method returns a pointer to the object instance, the incoming arguments are assigned to variables that hold pointers to their locations in memory. Thus, the (NSString *) data type designations all have asterisks. If you omit or forget the asterisks, the compiler will complain that the argument is defined with an incorrect data type.

Because the DGCar class has its own initialization method, the first task for that method is to send the “normal” init message to the NSObject superclass. You don’t necessarily need to know everything the method does in the superclass, but it is vital to completing the object’s instantiation. And, just as the DGCar class’s initialization method returns a reference to the instance being created, so does the NSObject’s init method. The DGCar method needs to capture that reference and apply it to itself. The way to do that is to assign the value returned by the NSObject’s init method to a DGCar property that references the instance object being defined by the current class (self):

    self = [super init];

Notice that this action is embedded within an if condition. The code confirms that the root object initialization was successful before continuing with the subclass initialization. If the superclass initialization were to fail, the condition expression would evaluate to nil, causing the nested statements to be skipped. Performing these kinds of success tests is good practice, even if the likelihood of failure approaches zero.

When the superclass initialization succeeds, parameter variables are assigned to the three instance variables that had been declared in the class’s interface. When you write the code in Xcode, the editor color codes instance variables that have been correctly declared (the default is green, but you can change color coding in Preferences). It is vital that the data types of the parameter variables match exactly the data types of the instance variable declarations. Xcode closely checks the consistency of data types as it builds an app. Mistakes will be highlighted before the app ever reaches the simulator.

The last statement of the DGCar initialization method returns a reference to the current instance—self. A return value’s data type must match the data type declared at the start of the method definition. In this case, the self keyword references an instance of the DGCar class being initialized here. The returned value could also be nil, which would occur if the superclass initialization failed. As you’ll see in a moment, the reference to a successfully created instance will be assigned to a variable elsewhere so that the instance can receive further messages.

Integrating the DGCar Class into Workbench

After you have entered the customized code into the DGCar header and method files, it’s time to integrate the class into Workbench and see it in action. The class has already been added to the Workbench project (when Xcode created the files), so click the WorkbenchViewController.m file to add code to the runMyCode: method that is to act with the DGCar class when you click the button in the simulator.

The first task is to add an import directive to the WorkbenchViewController implementation. You can put it above or below the existing #import statement, because neither depends on the other:

#import "WorkbenchViewController.h"
#import "DGCar.h"

The only changes to the rest of the implementation file are inside the runMyCode: method. Replace the original NSLog() function with four new statements:

- (IBAction)runMyCode:(id)sender {
    DGCar *myCar1 = [[DGCar alloc] initWithCarMake:@"Chevrolet"
                                             model:@"Malibu" year:@"2007"];
    DGCar *myCar2 = [[DGCar alloc] initWithCarMake:@"Ford"
                                             model:@"Taurus" year:@"2010"];

    NSLog(@"I used to drive a:%@", [myCar1 getFormattedListing]);
    NSLog(@"I now drive a:%@", [myCar2 getFormattedListing]);
}

Warning

This method also needs some memory management, but I’ll reserve that discussion until Chapter 6.

Now that you have a little more experience with Objective-C methods, I can address finer details of the runMyCode: method, which you added to the project in Chapter 3. This method, you’ll recall, runs in response to the Touch Up Inside event in the button that appears on the screen. Events trigger actions, and those immediate actions don’t return values. Such is the case of the runMyCode: method. Normally, a method that does not return a value specifies void as the return type (in parentheses before the method name). But because we wanted to associate this method with a button in Interface Builder, we specified the return type as IBAction. From a data typing point of view, IBAction is the same as void, meaning the method returns no value. The IBAction type (especially in the header file declaration) tells Xcode to be ready to connect with Interface Builder and offers the method as a choice when it comes time to connect the event to the code. You saw how runMyCode: “magically” appeared in the pop-up list of items when making the connection in Chapter 3 (see Figure 3-18 as a reminder).

The runMyCode: method has one argument, which is assigned to the parameter variable sender. Although I have chosen to compose this IBAction method with one argument, you can use one of three action method formats, as follows:

- (IBAction)methodName
- (IBAction)methodName:(id)sender
- (IBAction)methodName:(id)sender (UIEvent *)event

Your choice depends on whether you expect your action method to need a reference to the user interface control (the sender) and to more details about the event. The UIEvent object is analogous to the event object that is passed to JavaScript/DOM event handler functions. For Workbench, the sender argument is a reference to the button that the user clicks, and I chose this format for demonstration purposes. What may be confusing at first is that the data type is simply id, rather than some UIKit type, such as UIButton (the type of button used in the project). You’ll learn about the id data type in Chapter 6—it’s a cool feature of Objective-C.

Creating Object Instances

Each of the first two statements in the runMyCode: method creates an instance of the DGCar class (just as the JavaScript example created two instances of the car object):

    DGCar *myCar1 = [[DGCar alloc] initWithCarMake:@"Chevrolet"
                                             model:@"Malibu" year:@"2007"];
    DGCar *myCar2 = [[DGCar alloc] initWithCarMake:@"Ford"
                                             model:@"Taurus" year:@"2010"];

On the left side of the assignment operator are the variables that hold pointers to the object instances. Each is of data type DGCar. Notice that the data type of a variable being declared (as in the header file) or being assigned a value through an assignment operator is not enclosed in parentheses. The right side of the operator must evaluate to a DGCar object instance.

To reach the object instance value, the righthand expression sends two messages nested inside each other. As in JavaScript, when expressions are nested, the most deeply nested expression evaluates first. In this case, it is the [DGCar alloc] message. You can visualize that the nested message replaces itself with its result, after which the outer message executes.

The alloc method belongs to the NSObject superclass, and its job is to reserve a chunk of memory for use by the object instance. Because the DGCar class definition does not have an alloc method defined for it (Apple cautions programmers against overriding this method in custom classes), the message passes up the inheritance chain until it finds a match—in this case, one generation higher in the NSObject superclass. As you will learn in Chapter 6, whenever you allocate memory for an object via the alloc method, you are also responsible for recovering that memory when you’re done with the object.

NSObject’s alloc method returns a bare-bones instance of the object with a clean memory slate. To complete the instantiation, you must send the init method to this bare-bones instance so the various class members (instance variables and methods) can be written to the memory reserved for the instance. The value returned by [DGCar alloc] becomes the receiver of the initWithCarMake:model:year: message. Recall that the DGCar class sends the init method to NSObject as part of the custom initialization method. Thus, the custom initialization method of DGCar is killing multiple birds with a single call, causing the superclass’s init method to be called and assigning values to the instance’s three instance variables.

Once the DGCar class instances exist (myCar1 and myCar2), you can send them whatever other messages you have defined for them. You’ve defined only one other method, getFormattedListing, to stay in sync with the JavaScript version. To invoke that method for myCar1, the message is:

[myCar1 getFormattedListing]

For the purposes of the Workbench app, you’ll display the value returned by that method to the Xcode project console window via the NSLog() function, introduced in Chapter 3. You need to plug that returned value into the argument of NSLog().

NSLog() and String Formats

Recall that the initial NSLog() statement put into Workbench was as follows:

NSLog(@"Button was pressed.");

The argument is an Objective-C string literal, which begins with the @ symbol, followed by the quoted string of characters. But if you want to combine fixed text with values stored in variables or returned by methods, you can use string format placeholders, or specifiers. A specifier consists of a percent symbol (%) followed by a character or symbol indicating the type of data to be sent to the console. If the value is an object, the specifier combination is %@. Values to be plugged into the specifier appear after the quoted string, separated by a comma:

NSLog(@"I used to drive a:%@", [myCar1 getFormattedListing]);

Conveniently, you can use the same format specifier to display the contents of a wide range of object types. That’s because most Cocoa Touch object classes include a description method, which returns a meaningful string representing the object or its data (e.g., the contents of an NSArray object). If you’re looking to get a numeric value, however, you need some of the other specifiers, such as %d for an integer value or %f for a float value. Chapter 8 dives deeper into format specifiers.

You are free to stack up multiple specifiers and their fillers in a single NSLog() argument. The following statements present an alternative version that plugs in two strings derived from the same instance methods as used earlier and includes some escape carriage return characters (\n) to make the output display on three lines:

NSString *myCar1Description = [myCar1 getFormattedListing];
NSString *myCar2Description = [myCar2 getFormattedListing];
NSLog(@"My cars:\nThen - %@\nNow - %@", myCar1Description, myCar2Description);

This is how you will use NSLog() frequently when experimenting with Objective-C and Cocoa Touch in the Workbench app—using the Xcode console window to examine results or intermediate values.

Warning

Exercise moderation in distributing NSLog() functions throughout an app, and prepare for the day when you release the app. Although NSLog() functions don’t display content visible to the casual iPhone or iPad user, too many of them in the wrong places can slow down your final app. Alternatively, search the Web for iphone debug macro to see many ways to customize Xcode to display log statements only while debugging a project.

Running the Code

After you have made the modifications to the runMyCode: method in WorkbenchViewController.m file, you are ready to build and run it in the simulator. If the console window is not open, choose Console from Xcode’s Run menu. Then click the Build and Run button. When you click on the Workbench app’s button, you should see the results as shown in Figure 4-9 (your timestamps will be different, of course).

Viewing the results in the console window
Figure 4-9. Viewing the results in the console window

What About Accessing Instance Variables?

You may be wondering how the runMyCode: method can get or set individual instance variables in one of the objects. In JavaScript, it’s as easy as using the dot syntax to reference the object property, either for reading or assignment:

// setting a JavaScript instance variable
var car1Year = car1.year;
car2.year = "2009";

In Objective-C you have two ways to get and set instance variables: the traditional way and the modern (Objective-C 2.0) way. I’ll describe only the traditional way in this chapter, while Chapter 8 will show you the modern way.

If you design a class so that other classes of objects need to access its instance variables, the traditional approach calls for creating a matching set of getter and setter methods. In other words, the methods act as gateways to reading and writing values of instance variables. Doing this, of course, requires quite a bit more source code, but the result is an instance that can potentially be more reusable in other scenarios.

To demonstrate how this works, I’ll create a new class named DGCarAlternate (this will allow DGCar and DGCarAlternate to coexist side by side in the Workbench project without colliding with or relying upon each other). Example 4-4 shows the header file for the DGCarAlternate class. Six new method declarations (new items in bold) are for the getters and setters of the three instance variables (one pair for each variable). Notice that the method names for the getters are the same as the instance variable names. Additionally, notice that the setter method names begin with lowercase set, followed by an initial-capitalized variable name, as in setMake:.

Example 4-4. DGCarAlternate.h file
#import <Foundation/Foundation.h>

@interface DGCarAlternate : NSObject {

    NSString *make;
    NSString *model;
    NSString *year;

}

// Getters and Setters
- (NSString *)make;
- (void)setMake:(NSString *)newMake;
- (NSString *)model;
- (void)setModel:(NSString *)newModel;
- (NSString *)year;
- (void)setYear:(NSString *)newYear;

- (NSString *)getFormattedListing;

@end

Definitions of the actual methods are in the implementation file, as shown in Example 4-5 (new items are in bold).

Example 4-5. DGCarAlternate.m file
#import "DGCarAlternate.h"


@implementation DGCarAlternate

// Getter and Setter Methods
- (NSString *)make {
    return make;
}

- (void)setMake:(NSString *)newMake {
    make = newMake;
}

- (NSString *)model {
    return model;
}

- (void)setModel:(NSString *)newModel {
    model = newModel;
}

- (NSString *)year {
    return year;
}

- (void)setYear:(NSString *)newYear {
    year = newYear;
}

// Return formatted listing string
- (NSString *)getFormattedListing {
    NSString *result = @"";

    result = [NSString stringWithFormat:@"[%@] %@ %@", year, make, model];

    return result;
}

@end

Notice that I removed the custom initialization method from the DGCarAlternate class. Why? Because values will be assigned to the instance variables via setter methods rather than when the instance is allocated and initialized—merely a style choice for this demonstration. Example 4-6 shows the application of the DGCarAlternate class to the runMyCode: method in Workbench.

Example 4-6. The Workbench runMyCode: method designed for getters and setters
#import "WorkbenchViewController.h"
#import "DGCarAlternate.h"

@implementation WorkbenchViewController

- (IBAction)runMyCode:(id)sender {
    DGCarAlternate *myCar1 = [[DGCarAlternate alloc] init];
    DGCarAlternate *myCar2 = [[DGCarAlternate alloc] init];

    [myCar1 setMake:@"Chevrolet"];
    [myCar1 setModel:@"Malibu"];
    [myCar1 setYear:@"2007"];

    [myCar2 setMake:@"Ford"];
    [myCar2 setModel:@"Taurus"];
    [myCar2 setYear:@"2010"];

    NSLog(@"I used to drive a:%@", [myCar1 getFormattedListing]);
    NSLog(@"I now drive a:%@", [myCar2 getFormattedListing]);
}
...
@end

Notice that objects must still be instantiated via the alloc and init methods. But both messages pass up the inheritance chain through DGCarAlternate to the NSObject superclass (there is no alloc or init method defined in DGCarAlternate to intercept the messages).

Now that I have access to the instance variable getters, I can obtain those values individually from the runMyCode: method. For example, I can create a different format for the results sent to the console, as in the following:

NSLog(@"I used to drive a %@ %@ made in %@.", [myCar1 make],
    [myCar1 model], [myCar1 year]);

The setters also allow me to change the values of instance variables. Therefore, I can mix the first version of this class (with the custom initialization method that assigned values as the instance is being created) with additional code that changes one or more variables after instantiation.

Recap

We’ve covered a lot of ground in this chapter. The main goal was to introduce you to the different ways you need to begin thinking about objects and data in an iOS app, compared to your experiences with JavaScript. You have certainly seen some small similarities between JavaScript and Objective-C, such as assignment operators and notions of methods that optionally return values. But you have also seen that an iPhone or iPad app is largely a collection of class files, assisted by Interface Builder nib files (if you choose to go that way). Classes rule the day, and the basic structure of defining objects (through classes) entails writing more code, some of which will seem redundant based on your interpreted language experience. If you make a change to an instance variable name or method argument structure, you have to code the changes in both the header and implementation files.

The significance of the class structure should come into more focus in the next chapter. There, you’ll see how you’ll need to change your top-to-bottom interpretative conceptions to a completely different program execution flow model.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required