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.
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
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:
|
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:
can be replaced with
|
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:
|