Abhijit Joshi

ENGINEER. PROGRAMMER. ARTIST.

Objective-C: Introduction

Unlike most other Objective-C tutorials I found online, this journey begins without using Xcode right away. We will first learn the fundamentals of this language using the good old terminal window. Once we are comfortable with the syntax and quirks of the language, we will switch to Xcode to develop applications for OSX or for iOS.

You will still need to download Xcode. We just won't use the Xcode IDE for development right away. Goes without saying that you need to be on a Mac for all these tutorials.

Tutorial 001: Hello World!

Here is a simple code in Objective-C that you can copy and paste to a file called hello.m. As you can see, the overall syntax here looks very similar to C. In this first code example, the only things that you will notice as different from C are:

In fact, Objective-C is a strict superset of C, so anything you write in C will compile and run in Objective-C. 


#import <Foundation/Foundation.h>

int factorial(int N)
{
    return (N == 1)? 1 : N * factorial(N-1);
}

int main (void)
{
    // -----------------------------
    // print something to the screen
    // -----------------------------
    NSLog (@"Programming is fun!");

    // ----------------
    // add some numbers
    // ----------------
    int a = 10;
    int b = 20;
    int c = a + b;
    NSLog (@"a = %i, b = %i, c = a + b = %i",a,b,c);

    // -----------------------------------
    // calculate factorial using recursion
    // -----------------------------------
    int num = 6;
    NSLog(@"Factorial(%i) = %i",num,factorial(num));

    // -------------------------
    // dynamic memory allocation
    // -------------------------
    const int N = 10;
    int* array = malloc(N * sizeof(int));
    for(int i = 0; i < N; i++) {
        array[i] = i+i;
        NSLog(@"array[%i] = %i",i,array[i]);
    }
    free(array);

    return 0;
}

To compile this code, use the Makefile given below, with appropriately inserted tabs.


all:
    clang hello.m -o hello.x -framework Foundation
clean:
    rm *.x

If all goes well, you can run the code in the terminal window and will get the following output. Notice how the actual output of NSLog is preceded by additional details about the date, time and the name of the executable. This is probably because NSLog is mainly intended as a debugging tool. The "NS" actually comes from NextStep, the company that Steve Jobs founded after his brief departure from Apple. Apple later acquired NextStep and the NextStep operating system ended up forming the foundation of what we know today as OSX and iOS.

bash-3.2$ make
clang hello.m -o hello.x -framework Foundation
bash-3.2$ ./hello.x 
2013-12-30 16:01:14.096 hello.x[9617:507] Programming is fun!
2013-12-30 16:01:14.098 hello.x[9617:507] a = 10, b = 20, c = a + b = 30
2013-12-30 16:01:14.098 hello.x[9617:507] Factorial(6) = 720
2013-12-30 16:01:14.098 hello.x[9617:507] array[0] = 0
2013-12-30 16:01:14.099 hello.x[9617:507] array[1] = 2
2013-12-30 16:01:14.099 hello.x[9617:507] array[2] = 4
2013-12-30 16:01:14.099 hello.x[9617:507] array[3] = 6
2013-12-30 16:01:14.100 hello.x[9617:507] array[4] = 8
2013-12-30 16:01:14.100 hello.x[9617:507] array[5] = 10
2013-12-30 16:01:14.100 hello.x[9617:507] array[6] = 12
2013-12-30 16:01:14.101 hello.x[9617:507] array[7] = 14
2013-12-30 16:01:14.101 hello.x[9617:507] array[8] = 16
2013-12-30 16:01:14.101 hello.x[9617:507] array[9] = 18

Tutorial 002: Working with Classes

As the name suggests, Objective-C adds the capability to use object-oriented programming on top of regular C. So without further ado, we'll jump right in and show you how to define and use a simple class.

We'll create a class to work with Cartesian coordinates (x, y, z) in 3D space.

The established practice is to write the interface and implementation of the class in separate files, so this is what we will adopt as well. The project in tutorial 002 will consist of three source files:

The interface and implementation of the class is provided below.

Class Interface - CartesianPoint.h


#import <Foundation/Foundation.h>

// ---------------
// class interface
// ---------------

@interface CartesianPoint : NSObject
{
    // instance variables

    float x;
    float y;
    float z;
}

// method declarations

- (void) createCartesianPoint_using_x: (float) somex
                                and_y: (float) somey
                                and_z: (float) somez;

- (float) getx;

- (float) gety;

- (float) getz;

- (void) displayCoordinates;

- (float) distanceFromOrigin;

- (float) distanceFromCartesianPoint: (CartesianPoint*) somePoint;

@end

Class implementation - CartesianPoint.m


#import "CartesianPoint.h"

// --------------------
// class implementation
// --------------------

@implementation CartesianPoint

// method to initialize the instance variables
- (void) createCartesianPoint_using_x: (float) somex
                                and_y: (float) somey
                                and_z: (float) somez
{
    x = somex;
    y = somey;
    z = somez;
}

// methods to get instance variables
- (float) getx { return x; }

- (float) gety { return y; }

- (float) getz { return z; }

// method to display the instance variables
- (void) displayCoordinates
{
    NSLog(@"Coordinates are: (%.4f, %.4f, %.4f)",x, y, z);
}

// method to calculate the distance from the origin
- (float) distanceFromOrigin
{
    return pow(x*x + y*y + z*z, 0.5);
}

- (float) distanceFromCartesianPoint: (CartesianPoint*) somePoint
{
    float dx = somePoint.getx - x;
    float dy = somePoint.gety - y;
    float dz = somePoint.getz - z;
    return sqrt(dx*dx + dy*dy + dz*dz);
}

@end

How a particular user might use the class defined above is next. Note that the user does not need to have direct access to the instance variables of the class or to worry about the implementation-level details about how a specific method is implemented in the class. He or she simply needs to know the public interface of how to use the class. This is one of the principles of OOP, called as encapsulation.


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

int main (void)
{
    @autoreleasepool{

        // allocate memory to create instances of the CartesianPoint class
        // ALTERNATIVE: -  CartesianPoint *point1 = [[CartesianPoint alloc] init];
        CartesianPoint *p1 = [CartesianPoint new];

        // create point p1(1,1,1)
        [p1 createCartesianPoint_using_x: 1.0 and_y: 1.0 and_z: 1.0];

        // display (x,y,z) coordinates for p1 
        [p1 displayCoordinates];

        // print distance from point p1 to the origin(0,0,0)
        NSLog(@"Distance from the origin = %.4f", [p1 distanceFromOrigin] );

        // create a new point p2(10,10,10) and display its (x,y,z) coordinates
        CartesianPoint *p2 = [CartesianPoint new];
        [p2 createCartesianPoint_using_x: 10.0 and_y: 10.0 and_z: 10.0];
        [p2 displayCoordinates];

        // calculate the distance of point p2 from the origin
        NSLog(@"Distance from the origin = %.4f", [p2 distanceFromOrigin] );

        // calculate the distance of point p2 from point p1 
        NSLog(@"Distance from point1     = %.4f", [p2 distanceFromCartesianPoint: p1] );

    }

    // bye bye
    return 0;
}

Here is the Makefile for building everything:


# compiler used
CC = clang

EXE = CartesianPoint.x

# build targets

$(EXE): main.o \
    CartesianPoint.o
    $(CC) CartesianPoint.o main.o -o $(EXE) -framework Foundation

# compile dependencies

main.o: CartesianPoint.h main.m
    $(CC) -c main.m -o main.o

CartesianPoint.o: CartesianPoint.h CartesianPoint.m
    $(CC) -c CartesianPoint.m -o CartesianPoint.o

clean:
    rm *.o

veryclean:
    rm *.o *.x

And here are the results of building and running the code (with the date and time stamp edited out for brevity):

bash-3.2$ make
clang -c main.m -o main.o
clang -c CartesianPoint.m -o CartesianPoint.o
clang CartesianPoint.o main.o -o CartesianPoint.x -framework Foundation
bash-3.2$ ./CartesianPoint.x 
CartesianPoint.x[9739:507] Coordinates are: (1.0000, 1.0000, 1.0000)
CartesianPoint.x[9739:507] Distance from the origin = 1.7321
CartesianPoint.x[9739:507] Coordinates are: (10.0000, 10.0000, 10.0000)
CartesianPoint.x[9739:507] Distance from the origin = 17.3205
CartesianPoint.x[9739:507] Distance from point1     = 15.5885

Notes from the first two tutorials:

Manual memory management
This is what we typically do in C/C++. Every time we use memory on the heap, we need to make sure we free this memory after it is no longer required. Not doing this can lead to memory leaks.
Garbage collection
Some languages like Java and C# have garbage collection built in to the language specification. This means that memory allocated to objects no longer in use by the program is automatically reclaimed.

Tutorial 003: Using "Properties" to access Instance Variables of a Class

In the previous tutorial, we created a simple class for working with 3D Cartesian coordinates. Some of the methods we wrote were concerned with setting and getting the instance variables for objects of that class. For example, when the user wants to assign the coordinates (1,1,1) to object p1, we used:


// create point p1(1,1,1)
[p1 createCartesianPoint_using_x: 1.0 and_y: 1.0 and_z: 1.0];

If we need to get the individual coordinate values of the object p1, we wrote three "getter" methods:


// methods to get instance variables
- (float) getx { return x; }

- (float) gety { return y; }

- (float) getz { return z; }

If the user wants to get the x coordinate, all he or she needs to do is use


[p1 getx];   // returns 1.0

Writing our own setter and getter functions is a perfectly valid way of doing things, but Objective-C has an even simpler way of assigning and obtaining values of instance variables, using Properties. To illustrate how to use this feature, we will re-write the same class in Tutorial 002, without the getter and setter methods.

Here are the main differences from tutorial 002:

This is how the updated files now look like:

Class interface: CartesianPoint.h


#import <Foundation/Foundation.h>

// ---------------
// class interface
// ---------------

@interface CartesianPoint : NSObject
{
    // instance variables

    float x;
    float y;
    float z;
}

// declare that x, y and z can be used as a "property"

@property float x;
@property float y;
@property float z;

// method declarations

- (void) displayCoordinates;

- (float) distanceFromOrigin;

- (float) distanceFromCartesianPoint: (CartesianPoint*) somePoint;

@end

Class implementation: CartesianPoint.m


#import "cartesianPoint.h"

// --------------------
// class implementation
// --------------------

@implementation CartesianPoint

// automagically generate setter and getter functions
@synthesize x;
@synthesize y;
@synthesize z;

// method to display the instance variables
- (void) displayCoordinates
{
    NSLog(@"Coordinates are: (%.4f, %.4f, %.4f)",x, y, z);
}

// method to calculate the distance from the origin
- (float) distanceFromOrigin
{
    return pow(x*x + y*y + z*z, 0.5);
}

- (float) distanceFromCartesianPoint: (CartesianPoint*) somePoint
{
    float dx = somePoint.x - x;
    float dy = somePoint.y - y;
    float dz = somePoint.z - z;
    return sqrt(dx*dx + dy*dy + dz*dz);
}

@end

The main program is a little simplified now because we can use the dot notation for accessing instance variables of our class objects.

For example:

 
[p1 createCartesianPoint_using_x: 1.0 and_y: 1.0 and_z: 1.0];
 
can be replaced with

p1.x = 1.0; 
p1.y = 1.0; 
p1.z = 1.0;
 

Here is : main.m


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

// -------------
// main function
// -------------

int main (void)
{
    @autoreleasepool{

        // allocate memory to create instances of the CartesianPoint class
        CartesianPoint *p1 = [CartesianPoint new];

        // create point p1(1,1,1)
        p1.x = 1.0;
        p1.y = 1.0;
        p1.z = 1.0;

        // display individual coordinates
        NSLog(@"p1: x = %.4f", p1.x );
        NSLog(@"p1: y = %.4f", p1.y );
        NSLog(@"p1: z = %.4f", p1.z );

        // display (x,y,z) coordinates for p1 
        [p1 displayCoordinates];

        // print distance from point p1 to the origin(0,0,0)
        NSLog(@"Distance from the origin = %.4f", [p1 distanceFromOrigin] );

        // create a new point p2(2,2,2) and display its (x,y,z) coordinates
        CartesianPoint *p2 = [CartesianPoint new];
        p2.x = 10.0;
        p2.y = 10.0;
        p2.z = 10.0;
        [p2 displayCoordinates];

        // calculate the distance of point p2 from the origin
        NSLog(@"Distance from the origin = %.4f", [p2 distanceFromOrigin] );

        // calculate the distance of point p2 from point p1 
        NSLog(@"Distance from point1     = %.4f", [p2 distanceFromCartesianPoint: p1] );
        
    }

    // bye bye
    return 0;
}

The Makefile is unchanged from Tutorial 002. Also, you can confirm for yourself that the results are identical to those obtained after running the code from Tutorial 002.

To summarize what we learned in Tutorial 003:

@property
This keyword is used in the class interface and can be applied to some or all instance variables of a class. These instance variables are then treated as properties.
@synthesize
This keyword is used in the class implementation and is typically applied to instance variables that have been designated as properties. Behind the scenes, the compiler automatically generates the setter and getter (or accessor) methods for these instance variables.
dot notation
The dot notation (.) can be used to access instance variables designated as properties. This simplifies coding for the developer.