The iPhone API uses the Objective-C language. Objective-C is an application of principles from Smalltalk to the C language. The idea was to combine the power of the C language, which is an industry standard, with the object-oriented programming approach of the Smalltalk language. The result is a language that might be confusing at first, but it makes sense once you understand a few concepts.
The first thing to know is that Objective-C is a strict superset of C. Anything that you can write in C you can compile using the Objective-C compiler. C-style structs, function calls, memory allocation, and pointers are all available. However, in addition to all of the normal C language syntax, Objective-C adds some of its own features as well, such as thread synchronization, try-catch blocks, and garbage collection. The primary improvement provided (and the reason for the language) is an object-oriented structure.
Classes in Objective-C are conceptually very similar to Java and C++ classes. A class is an object that contains
data, called member variables, and
functions, called methods. Just like in Java
and C++, classes are usually defined before they can be used (although
in Objective-C, they can also be created at runtime). You define a class using the @interface
keyword. Similar to C++, Objective-C classes should be defined in
an .h file, while their
implementations should be separated into an .m file. Also as with C++, you may use the @public
or @private
keyword to denote the level of syntax
protection of the following lines. Objective-C is different from C++ or
Java, however, in that it defines its methods outside the curly braces
of the class. The methods are associated to the previous class and
terminated by an @end
keyword.
The following code creates a class named Animal
:
@interface Animal { // Member Variables go here @private int foo; } // class Methods go here, outside the braces - (int) bar: (double) input1; @end
Notice the interesting way methods are defined: they begin with a −
for normal functions (or a +
for class methods, which we will discuss
shortly), followed by the return type of the function in parentheses and
the name of the function. If the function has parameters, each is
written with a parameter description followed by a colon, the parameter
type in parentheses, and the parameter name (except for the first
parameter, which uses the function name instead of a parameter
name).
Here is the same class in C++; note the semicolon required at the end:
class Animal { // Member Variables and Methods go here private: int foo; public: int bar( double input1 ); };
Once more for Java:
class Animal { // Member Variables and Methods go here private int foo; public int bar( double input1 ) {} }
All three languages can use //
for single-line comments.
And just as in Java and C++, classes can extend or
inherit other classes. A common base class that
almost all Objective-C classes inherit from is NSObject
, which we will explain later. The syntax for inheritance is
similar to C++, except that public
and private
keywords are not used in
front of the parent class:
@interface Animal : NSObject { } //Objective-C inheritance @end class Animal : public NSObject { //C++ inheritance }; class Animal extends NSObject { //Java inheritance }
It is important to note that just like Java Objective-C cannot
inherit from multiple classes. Say you want to create a class Animal
that inherits NSObject
, but also inherits a class GameEventListener
, which allows it to respond
to special events in your game. In C++, you could define GameEventListener
as an abstract base class
and inherit from both. However, in Java, you would have to define
GameEventListener
as an interface
class, and in Objective-C, you would define it as @protocol
. A protocol in Objective-C is similar to an abstract base
class in C++, except that it must be absolutely virtual. It cannot have
any member variables or method implementations. Just like interfaces in
Java, a class in Objective-C may implement as many protocols as
necessary.
Here is the implementation in C++:
class GameEventListener { // Member Variables, Methods and Virtual Methods go here private: bool active; public: bool isListening(); //returns true if active is true virtual void handleEvent( int event ) = 0; }; class Animal : public NSObject, public GameEventListener { // Member Variables and Methods, and Override Methods go here // any pure virtual functions from GameEventListener must be implemented private: int foo; public: int bar( double input1 ); void handleEvent( int event ); // isListening() already implemented, no need to override };
Here is the same implementation in Java:
interface GameEventListener { // interfaces may not contain Member Variables // Method definitions go here, they cannot be implemented yet public bool isListening( ); public void handleEvent( int event ); } class Animal extends NSObject implements GameEventListener { // Member Variables, Methods and Override Methods go here private int foo; private bool active; //must be defined here public int bar( double input1 ) {} public bool isListening( ) {} public void handleEvent( int event ) {} }
The Java interface class is called a protocol class in Objective-C, so we define it
using @protocol
:
@protocol GameEventListener // protocols may not contain Member Variables, no curly braces // Method definitions go here, they cannot be implemented yet - (BOOL) isListening; - (void) handleEvent: (int) event; @end @interface Animal : NSObject <GameEventListener> { // Member Variables go here @private int foo; BOOL active; } // Methods and Override Methods go here - (int) bar: (double) input1; - (BOOL) isListening; - (void) handleEvent: (int) event; @end
Note
Programmers who are experienced in using template classes in C++ or generics in Java may get confused here. Although the syntax for using protocols in Objective-C looks like the syntax for templates in C++ and generics in Java, it is not the same thing.
As we mentioned earlier, function implementations are separated into .m files. The C++ implementation of the example would look like this:
bool GameEventListener::isListening() { return active; } void Animal::bar( double input1 ) { //do something with input1 } void Animal::handleEvent( int event ) { //do something with event }
And the Objective-C implementation would look like this:
@implementation Animal @synthesize foo; - (void) bar: (double) input1 { //do something with input 1 } - (BOOL) isListening { return self.active; } - (void) handleEvent: (int) event { //do something with event } @end
Once you have defined a class, you will want to create an instance of it.
In both C++ and Java, the instance would look like this:
Animal* beaver = new Animal();
While Objective-C would use this:
Animal* beaver = [Animal alloc];
The use of those brackets around the call to alloc
is known as
messaging, one of the interesting features of
Objective-C. It is basically the way you call functions on Objective-C objects. Although the syntax
may be new, the concept is the same as a function call in C++ or Java. The preceding example calls the new
function on the Animal
class, which will return a newly
allocated instance of that class.
Predictably, you could now call functions on that object, like so:
BOOL listening = [beaver isListening];
In C++, the preceding code would look like this:
bool listening = beaver->isListening();
And in Java, it would look like this:
boolean listening = beaver.isListening();
Sending parameters is also possible. Parameters are separated by colons:
[beaver handleEvent: EVT_EXAMPLE ];
In C++, the preceding code would look like this:
beaver->handleEvent( EVT_EXAMPLE );
And again in Java:
beaver.handleEvent( EVT_EXAMPLE );
One of the additions of messaging is keywords. Each parameter
after the first is preceded by a keyword. If the Animal
class also had a function called
findFood
that expected an amount of
food to find and a flag to indicate only vegetables were allowed, you
might see this:
[beaver findFood: 50 vegetablesOnly: true];
The same function in C++ would look like this:
beaver->findFood(50, true);
Although this makes function calls use more room on the screen, it also attempts to make reading them easier. You do not need to look up the function definition or rely on an IDE’s code completion feature to understand the nature of the values passed in.
You can also nest messages by inserting another set of brackets
where a parameter would go. If the Animal
class had a function BOOL -isVegetarian
, you might see this:
[beaver findFood: 50 vegetablesOnly: [beaver isVegetarian]];
In C++, it would look like this:
beaver->findFood(50, beaver->isVegetarian());
Note
The Objective-C equivalent to a const or
factory method in C++ or Java is
to use +
in front of the method
name in the @interface
section.
This allows you to call the function on the class itself without the
need for an instance of the class.
For example:
@interface Animal + (BOOL) canAnimalsTalk; @end
can be called this way:
[Animal canAnimalsTalk];
These are called class methods in Objective-C, since they are called directly on the class itself instead of an instance of the class. Functions declared this way cannot make use of the class’s member variables.
Objective-C provides a number of ways to access class member variables. The
first way uses messaging, where the name of the function is the same as
the name of the member variable being accessed. This is called
an accessor function, and it would be
similar to writing get
and set
functions in C++ or Java. The functions
are created automatically when you use the @synthesize
keyword in
the @implementation
section of the
class and @property
when defining it
in the .h file.
The usage syntax looks like this:
int example = [beaver foo]; //returns the value of int foo [beaver setFoo:10]; //sets the value of int foo to 10
Notice that the name of the automatically generated setter method
is set
followed by the variable’s
name with the first character in uppercase. You can also use dot notation for the same purpose, like so:
int example = beaver.foo; //returns the value of int foo beaver.foo = 10; //sets the value of int foo to 10
Note
Just like the this
keyword in
C++ and Java, the self
keyword is
available inside Objective-C class methods.
Since Objective-C is a superset of C, all of the rules regarding malloc
and free
still apply. On the iPhone, there is no
Garbage Collector that takes care of all objects for you, like the one
in Java does, so it is possible to have memory leaks if allocated memory
is not properly deallocated.
However, Objective-C does implement a reference counter inside the
base class NSObject
. Therefore, any class that
inherits from NSObject
(and most
will) has the same reference counting functionality built-in.
Specifically, the copy
, new
, retain
, release
, autorelease
, and alloc
methods provided by NSObject
are what get the job done.
Whenever an NSObject
subclass
instance is created using the alloc
method or any function with new
or
copy
at the beginning of its name, it
will be created with a reference count of 1. You can use the retain
function to increment the value by one,
and the release
and autorelease
functions to decrement the value
by one. Once the reference value has reached 0, the object will be
removed from memory.
If you call retain
on an
object, you will leak memory if you do not also call release
or autorelease
on that object because the
reference count will never reach 0.
Like C++, Objective-C classes support the concept of constructor and destructor
functions, but with some slight variation. When an object is allocated,
the programmer must call the initialization function manually. By
default, initialization functions are named init
.
The following code is typical of Objective-C:
Animal* beaver = [[Animal alloc] init];
When an Objective-C class is destroyed from memory, its dealloc
function will be called, similar to a
destructor. If you overload the dealloc
function in
your class, you must also call the dealloc
method of the superclass or it may
leak memory:
-(void) dealloc { //perform destructor code here [super dealloc]; }
Because Objective-C and Interface Builder are used together for iPhone apps, two
macros have been included to allow you to link Objective-C code to
Interface Builder views: IBOutlet
and IBAction
.
By putting IBOutlet
in front of
UI variables and IBAction
in front of
class methods, you allow Interface Builder to know what variables and
functions your code has made available for its use:
@interface myWindow { IBOutlet UIImageView *backgroundImage; IBOutlet UIWindow *window; IBOutlet UISwitch*soundToggle; } - (IBAction) playgame; - (IBAction) toggleSound:(id) sender; - (IBAction) handleEvent:(id) sender forEvent:(UIEvent*) event; @end
At compile time, IBAction
is
replaced with void
, and IBOutlet
is simply removed. They are used only
to determine which methods and variables show up in Interface Builder
and have no runtime effect.
Because Objective-C is a superset of the C language, you can write portions of your code entirely in C. But the Objective-C compiler also allows you to use C++ in your projects. It is possible to use C, C++, and Objective-C syntax in the same file. Files that contain C++ implementations should use the extension .mm instead of .m.
Get iPhone Game Development now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.