1.5. Transitioning to Objective-C

Objective-C was written by scientist and software engineer Brad Cox in the early 1980s. 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 both 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 also 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, you may be transitioning from C, C++, or another similar language. 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.

1.5.1. Messaging

The first thing you'll notice in Objective-C is the heavy use of brackets. In Objective-C, methods are not called in a traditional sense; their objects are sent messages. Likewise, a method doesn't return, but rather responds to the message.

Much of this can be chalked up to semantics, however, at least in terms of what the developer experiences. Your application's program flow is nothing alien to a C or C++ program: when you invoke a method, your program waits for a response before continuing, allowing you to assign the return value or invoke methods as part of conditional statements.

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, or test to see whether an object responds to a particular message. The downside to this is that it's entirely possible to send an object a message to which it isn't programmed to respond, causing an exception and likely program termination. For example, you can compile code that will send the following message to an object:

[ myObject heyWhatsUpHowYouDoin ];

At runtime, the application will raise an exception unless a method exists named heyWhatsUpHowYouDoin to respond to the message. Of course, the advantage to this is that a method might exist for the object, but be undocumented in the class's prototypes. We'll show you just a few examples of undocumented methods like this in Chapter 3.

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 the following:

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 argument data types to be declared—polymorphism on steroids:

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

1.5.2. Class and Method Declarations

While you can define C++ classes 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 you specifically declare the interface declaration in its own code block (called @interface) separate from the block containing the implementation (called @implementation). You construct and name methods in a Smalltalk-esque fashion, and they somewhat resemble regular C functions.

The interface for our widget example might look like Example 1-1, which is a file named MyWidget.h.

Example 1-1. Sample interface (MyWidget.h)
#import <Foundation/Foundation.h>

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

 

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

1.5.2.1. The "id" data type

In the example just shown, the id data type is defined as the return type by the alloc method. This data type is a generic type used to reference any arbitrary object. Think of the id data type as the Objective-C version of a void pointer. Wherever specified, any object can be used. You'll see this data type used throughout the book, especially in cases where a delegate is assigned to an object to receive special notifications of events.

1.5.2.2. Imports

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

#ifndef _MYWIDGET_H
#define _MYWIDGET_H
...
#endif

1.5.2.3. Interface declaration

The interface for a class is declared with the @interface statement followed by the interface's name and the base class (if any) it is derived from. At the end of your class declaration, you'll end the block with the @end statement. Within this block, you'll declare all class variables and methods for the class.

1.5.2.4. Methods

Methods should be 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. Static methods don't need to be invoked for any particular object; they are methods that represent the entire class in general—for example, "the Widget class". Instance methods are invoked for a named instance of a class—for example, "my Widget" and "his Widget".

The alloc method is a good example of a static method. It is responsible for allocating and returning a new instance of the given class. In your code, you'll call this method whenever you create a new object by referencing the class directly, for example MyWidget. Instance methods that are specific to an instance of the MyWidget class, such as setSpeed and powerOn, should be invoked by referencing the named object. Many Cocoa classes provide static and instance methods for initializing objects.

Every declared parameter for a method specifies a data type, a local variable name, and an optional external variable name. Examples of external variable names in Example 1-2 are withMass and withGyroscope. The notifier (calling method) that invokes the method refers to method's external (public) variable names, but inside the method the arguments are referenced using their local variable names. Thus, the setSpeed method uses the local _mass variable to retrieve the value passed as withMass.

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

1.5.3. Implementation

The suffix for an Objective-C source code file is .m. A skeleton implementation of the widget class from the last section might look like Example 1-2, which is named MyWidget.m.

Example 1-2. Sample implementation (MyWidget.m)
#import "MyWidget.h"

@implementation MyWidget

+ (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 a single code block, the implementation begins with an @implementation statement and ends with @end.

In C++, it is common practice to prefix member variables so that public methods can accept the actual name of the variable. This makes it easy to reuse someone else's code because you can deduce a variable's purpose by its name. Since Objective-C allows you to use both an internal and external variable name, a 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 referenced as a parameter of the method, while the method's local variable name is prefixed with an underscore; e.g., _speed.

Consider the example setSpeed method just shown. When invoked, its external argument name, withMass, is used:

[ myWidget setSpeed: 1.0 withMass: 2.0 ];

Inside the method's code block, the internal variable name, _mass, is used:

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

1.5.4. Properties

Objective-C 2.0 introduced the concept of properties. Properties are an intermediary between instance variables and methods; they add a syntactic convenience by allowing the developer to address variables directly, rather than through separate setter/getter methods. Properties are similar to public variables, but allow the behavior of their access to be defined. Additionally, properties invoke methods that can be overridden when the property is read from or written to.

To work with an instance variable in earlier versions of Objective-C, you would typically write two methods—one to read the variable (a getter) and one to write (a setter):

BOOL myVariable = [ myClass variable ];     /* getter */
[ myClass.setVariable ] = YES;       /* setter */

Properties allow the developer to change his syntax to address the property's name for both operations. This changes the code to look more "C-ish":

BOOL myVariable = myClass.variable;
myClass.variable = YES;

You can define properties with a number of different storage semantics. These include assign, retain, and copy. You can also define properties as readonly. Define a property for a variable named size in the class definition as follows:

@interface MyClass : BaseClass
{
    int size;
}
@property(copy) int size;

In the class's implementation, use the @synthesize statement to implement the property:

@implementation MyClass
@synthesize size;
...
@end

When the @synthesize statement is used, a getter and setter method are automatically generated internally if they don't exist in your code. These methods are transparently called whenever the property is accessed:

myClass.size = 3;

You can also define custom setter and getter methods without using @synthesize:

-(int)myGetter;
-(void)setSize:(int)size;

@property(copy,getter=myGetter) int size;

Many methods you may have seen in previous versions of Objective-C code have been replaced with properties in the iPhone SDK.

1.5.5. Protocols

A protocol is a set of methods that an object agrees to implement in order to communicate with another object. You'll use many protocols throughout this book known as delegate protocols. Delegate protocols are protocols that notify an object about events occurring in another object. To implement a protocol, your interface declaration simply declares that it supports the given protocol. This way, other classes requiring a certain protocol can expect that your class will respond to the methods used in the protocol. A class can implement any number of protocols. You'll receive a compiler warning if your implementation is incomplete.

As an example, presume that a BaseWidgetDelegate protocol is a protocol you've designed to alert an object of events occurring within a given widget, such as a powering on event or the event of its power settings changing.

Use the protocol statement to define the methods used in the protocol. You can specify the methods that are mandatory to implement the protocol using the required statement:

@protocol BaseWidgetDelegate

- (void)baseWidgetDidChangePowerSettings:(float)newPowerLevel;

@required
- (void)baseWidgetDidGetTurnedOn:(id)widget; /* This method is required */

@end

A class looking to send these types of notifications might define a variable containing the pointer to an object to receive notifications:

@interface MyWidget : BaseWidget {
    id delegate;
}

They can define a property for its delegate and require that the object assigned to it implement the BaseWidgetDelegate protocol:

@property(assign) id<BaseWidgetDelegate> delegate;

The class desiring to receive notifications must now implement the BaseWidgetDele⁠gate protocol to be assigned as the widget's delegate. The example to follow declares a WidgetManager class and specifies the protocol be implemented within the class:

@protocol BaseWidgetDelegate;
@interface WidgetManager : NSObject <BaseWidgetDelegate>
...
@end

The only thing remaining is to implement the actual protocol methods in the class's implementation.

Once implemented, your application code can assign the widget manager class to the widget's delegate property without generating a compiler warning, because it implements the BaseWidgetDelegate protocol:

myWidget.delegate = myWidgetManager;

1.5.6. Categories

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

You can place a category class "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 class' source code. Categories allow you to expand base classes 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 legacy code using the base class directly will see only the original version.

You might be thinking about inheritance as an equally viable solution. The difference between categories and inheritance is the difference between performance-tuning your car versus dressing it up as a parade float. When you tune up your sports car, you add new components to the internals of the vehicle, which cause it to perform differently. You may even pull out some components and replace them 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 only appears different. In the context of a parade, the vehicle is a completely different animal to spectators, but when you take it to the mechanic, it's the same old stock car you've been driving around.

As an example, the widget factory is coming out with a new type of widget that can fly through space, but is concerned that making changes to the base class might break existing applications. By building a category, the developers ensure that 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 1-1 to carry out inheritance:

#import "MyWidget.h"

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

A complete source file implementing the category is shown in Example 1-3.

Example 1-3. Sample category (MySpaceWidget.m)
#import "MySpaceWidget.h"

@implementation MyWidget (MySpaceWidget)

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

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

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

1.5.7. Posing

In Objective-C, a subclass can pose as one of its super-classes, 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, you created some mechanical widget classes. Well, at some point after designing all of these widgets, you developed a need to perform advanced diagnostics. Rather than rip out functions in the original class, you've decided to create a new subclass named MyDiagnosticsWidget. See Examples Example 1-4 and Example 1-5.

Example 1-4. Sample interface for posing (MyDiagnosticsWidget.h)
#import <Foundation/Foundation.h>
#import "MyWidget.h"

@interface MyDiagnosticsWidget : MyWidget
{

}

- (void)debug;
@end

Example 1-5. Sample implementation for posing (MyDiagnosticsWidget.m)
#import "MyDiagnosticsWidget.h"

@implementation MyDiagnosticsWidget

- (void)debug {
    /* Generate debugging information */
}
@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:

MyDiagnosticsWidget *myDiagWidget = [ MyDiagnosticsWidget alloc ];
    MyWidget *myWidget = [ MyWidget alloc ];
    class_poseAs(myDiagWidget, myWidget);

Now, any other methods you've replaced in the posing class would pose as if they were from the original base class.

1.5.8. Additional Resources

As you read many examples in this book, you'll see references to objects used in Apple's Cocoa environment. Most of these objects will begin with the prefix NS, such as NSError or NSString. The Cocoa environment provides a number of standard objects like this to handle arrays, strings, and other such objects; many are available both with the iPhone and the Mac OS X desktop environments. We'll show you how to work with some of these objects in relation to the example at hand, however entire books have been written just to document Cocoa classes. If you run into a class you're not familiar with, Apple's online documentation can provide you with a complete explanation. The Apple Developer Connection website hosts a Cocoa reference available at http://developer.apple.com/reference/Cocoa/. From here, you can read a reference for a particular topic or enter the class's name into the search window to be taken directly to the class's documentation.

To learn more about Cocoa and Objective-C programming, you may also check out the following great resources from O'Reilly:



Learning Cocoa with Objective-C, Second Edition, by James Duncan Davidson and Apple Computer, Inc.

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



Objective-C Pocket Reference by Andrew M. Duncan

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