Copyright © 2003 Dennis Leeuw, Pascal Bourguignon
Permission to use, copy, modify and distribute the Beginners Guide to Objective-C Programming and its accompanying documentation for any purpose and without fee is hereby granted in perpetuity, provided that the above copyright notice and this paragraph appear in all copies.
The copyright holders make no representation about the suitability of this Guide for any purpose. It is provided "as is" without expressed or implied warranty.
Look what they done to my song ma | ||
--Melanie Safka |
Objective-C is an object oriented programming language. It is not a standardized language, that is, there is no official standard that describes Objective-C. Brad J. Cox developed the original Objective-C language, by adding SmallTalk-80 extensions to C.
Objective-C is ANSI C with a relatively small set of smalltalk-like object oriented syntax grafted on to it. It is considered to be the most dynamic of the C based OO languages.
The Objective-C runtime is part of the GNU C compiler (gcc). And also part of the NeXT and Mac OS X compilers.
Dennis Gladding wrote the first GNU version in 1992, shortly followed by the second version by Richard Stallman. In 1993 the next version appeared written by Kresten Thorup who also used this version for the actual NeXT implementation. Later versions where maintained by Ovidiu Predescu and currently (2001) Stan Shebs of Apple Computer Inc. is the responsible person.
Objective-C highligts
Protocols. A protocol declares the methods which should be implemented on a class which defines a given protocol. Since Objective-C lacks multiple inheritance, we can use Protocols to make up for this.
Categories. Categories allow you to add or repair functionality of a given class without having access to its source code. Whenever an object of the type you added the category to is instantiated within your program, it has the extra methods you added to it.
Almost every document I have found on Objective-C programming assumes you already know about C programming or object oriented programming. And if you are like me, a non-programmer that once in a while writes a shell script, that is frustrating. So on a nice and shiny friday afternoon I decided to write a book about Objective-C programming, for the non-programmer. A book that I would like to have myself.
Objective-C is an extension to the C programming language, which makes C object oriented. This is nice, but actually not something you want to know. You want to write a program that works. So this book will take the practical route. Through examples I will try to guide you, and myself, through the first steps of creating an Objective-C program.
No prior programming knowledge is assumed and references will be given to further documentation, since not everything you want to know will fit in this book. I will try to get you started, not master you in Objective-C.
Every program you use on your computer is written in a language that humans can understand. Then it is translated to a language a computer can understand. The language you use to write programs is called a programming language and the piece that the computer understands is called a binary. The process of translating the one into the other is known as compiling. So you need a compiler to use the programs you write.
The idea about this book is is that everybody wanting to learn Objective-C should be able to do so. The tools that I use in this book should thus be available to you too. The simplest solution I could come up with is to use the Free Software Foundation tools from their GNU project. This means that I will use in every example the Free Software Foundations GNU Compiler Collection (gcc) to compile programs.
This compiler is used on many systems. Most Unix systems have gcc and also Apple Mac OS X has gcc as its default compiler.
Next to that I will assume you have the GNU make installed. On certain systems GNU make might be present as gmake. In that case substitute all make by gmake
I could not have written this book, without the enormous amount of help of just one single person: Pascal Bourguignon. When I asked a single question he wrote pages of information to help me understand C, Objective-C and even SmallTalk. Pascal invested, I guess, more or less the same amount of time in this writting as I did. For that I am very grateful and it shows the open spirit in which Open Source projects flourish.
A lot of notes on the code I received from Chris B. Vetter. I also would like to thank him for his always possitive, constructive remarks that helped make this Guide what it is.
Once you understand the basic concepts, the actual learning starts. Come out and ask questions, read online documentation and good books. A quick reference is the books and sites I used to write this document.
GNUstep's Objective-C Language and GNUstep Base Library Programming Manual
The C Programming Language Second Edition by Brian W. Kernighan and Dennis M. Ritchie ISBN: 0-13-110370-9
The C programming book, written by the inventors of C, start with a program that prints hello, world on your screen. I figured that it might be a nice start to do just that.
I will give you the code first, and then we will look at what all the parts mean.
/* Comments: ** Example written by Pascal Bourguignon ** return status change by Chris B. Vetter ** Added comments in empty {} section by Chris B. Vetter ** int main() changed to int main(void) by Dennis Leeuw ** from comments by Chris B. Vetter */ #include <objc/Object.h> @interface Greeter:Object { /* This is left empty on purpose: ** Normally instance variables would be declared here, ** but these are not used in our example. */ } - (void)greet; @end #include <stdio.h> @implementation Greeter - (void)greet { printf("Hello, World!\n"); } @end #include <stdlib.h> int main(void) { id myGreeter; myGreeter=[Greeter new]; [myGreeter greet]; [myGreeter free]; return EXIT_SUCCESS; } |
It all looks very complex, for now just use your favourite editor and make sure you copy the program as is. Write it to disk with the name hello.m
File extensions
There are a couple of rules to the extensions of files, to make life easier. With the correct use of extensions, the compiler can easily detect what kind of language is needed. Ofcourse you could use gcc -x <language>, to tell gcc what kind of input file it is, but using the correct extension is easier. Next time you see a file, you yourself know in what kind of language it is written.
Leave your editor and type on the command line:
bash-2.05a$ gcc -lobjc hello.m -o hello |
You should not see any errors. If you do you probably do not have the Objective-C part of gcc installed. Please make sure you have it installed and working before you proceed.
If the command prompt returned without any messages, then you are fine. It worked and you have now your first working Objective-C program! Type on the command prompt the following, to see that I am right:
bash-2.05a$ ./hello Hello, World! |
We will dig through the program, from top to bottom to see what we have done.
/* Comments: ** Example written by Pascal Bourguignon ** return status change by Chris B. Vetter ** Added comments in empty {} section by Chris B. Vetter ** int main() changed to int main(void) by Dennis Leeuw ** from comments by Chris B. Vetter */ |
Another way of commenting your source is by the use of //. This is not a container like /* and */ which comments everything between the two, but // comments everything from the double slash, until the end of line. I commented the following code with this method.
#include <objc/Object.h> // Includes the gcc Object.h header file |
Every program you will ever write uses atleast functions and variables. Functions tell a program what to do and variables hold the data with which the functions work. In object-oriented languages, variables and functions are gathered together in objects.
Objects consist of instance variables (data) that represent the state of an object, and methods (functions) that act on these variables in response to messages. One can see an object as a simple program that you can call (send a message to) to perform a simple action.
With the above paragraph I introduced a couple of the terms used in object-oriented programming. This section will try to make you familiar with the meaning of the above terms.
To make life easier you will often use functions and objects written by others. To tell the compiler that you will use a function not available in this file you use #include. A lot of documents you will read later, might tell you to use #import, but that is not used anymore. You always need to use #include.
In this example we included Object.h, which is provided by the compiler.
The fact that you needed the Objective-C part of gcc to compile the program, has to do with Object.h. The Object definition written in Object.h is the basis for all objects. It is said that Object is the root class of all classes.
Objects, Classes and instances
If we are a school for actors we have a written definition of what an actor is. From the The cambridge dictionary a description might be: "someone who pretends to be someone else while performing in a film, theatrical performance, or television or radio programme".
With the above we have defined a class. In this case everyone that fits within the above description is an actor. The oposite is also true, everyone who wants to be an actor has to perform the above definition.
Assume a girl taking a course at our school. When she finishes our school she will become an actress. Which is equal to the compiling we did. When the compiling succeeds we have created a class object, namely an actress, able to perform any role. As long as the compiling is not successful the lessons aren't finished.
But as the above definition says she will only be a true actress, when she performs, she has to perform in e.g a play. Since every play is different she has to learn a role (get specific, play related, information: instance variables). When she plays the role she has then become our object.
Our actress of course is also female, so her root class is females. And she inherits from her female root class all methods that make her female.
As with Object.h which is just the definition of an object, we also need to tell the compiler what we are going to do. We first need to define what the compiler is going to expect. For that Objective-C has the @interface/@end construction. This part is normally saved in a .h file, but I choose to first present you with the entire file. Later we will split things up.
@interface Greeter:Object { /* This is left empty on purpose: ** Normally instance variables would be declared here, ** but these are not used in our example. */ } - (void)greet; @end |
The first line tells the compiler that Greeter is a subclass of Object
, so it inherits everything directly from the root class Object.
Inheritance
As seen above inheritance is a very powerful Objective-C functionality. With just one line, we included all methods and data from the Object class.
Then we add the method greet
which makes our Greeter special. The difference between Object and Greeter is the method greet.
What we did was using the general Object class and tell the compiler that from now on everything an Object can do Greeter can do too. So Greeter is identical to Object. But that is not what we want. We want Greeter to be a special Object. That is what the - (void) greet;
is about.
With the - (void) greet;
we extend our Greeter object with an additional functionality. Next to all the Object methods, it now also knows the method greet
.
Methods
Methods are the functionalities that an object can perform. Objects that we write have two kinds of methods: The methods that are inherited from the root class and the ones that we have added.
On our school we will educate our girl the methods of an actress, which equals to programming a class (we write the methods). When she finishes our school she is an actress. Her exam is our compiling stage.
Now that we defined our Object we can close the definition with @end.
The next section we are going to look at, is the implementation section. This will describe what our object Greeter does. Since we inherited all functions available in Object, we do not need to write them (now you must start to feel all warm and fuzzy about object oriented programming). We only need to tell the compiler the things that make our object Greeter so special:
#include <stdio.h> @implementation Greeter - (void)greet { printf("Hello, World!\n"); } @end |
This section tells the program what to do, namely to print to our screen the words Hello, World!, followed by a new line. Remove the \n from the print-statement, recompile the code and execute. Now you know why the \n was used.
The first thing you might notice is the fact that another #include is present. Normally you would group the #includes at the beginning of your program, but I had a special reason to put it here, which I will explain in a following section, when we split up our little program.
As the inclusion of Object.h, added Object functionality to our program, so does stdio.h add the standard input/output functions. You should take for granted now that this inclusion adds the function printf
which we need later.
And the last section is what makes the program work. Every program has a special function called main
. Normally you are free to call your functions whatever you want, as long as you do not call them main
. With main
you tell the compiler: Hey you, my program starts here! The rest of this chapter will describe the main
part of our little hello.m file.
#include <stdlib.h> int main(void) { id myGreeter; myGreeter=[Greeter new]; [myGreeter greet]; [myGreeter free]; return EXIT_SUCCESS; } |
The first important things are the { and } symbols. They are called the block statements. Normally after each function a single statement is allowed. In our example the main-function you see has 5 statements, the ones ending with ;. To overcome the limitation of a single statement, we group the statements together to form a single one by enclosing them between { and }. This will in a later chapter be explained in more detail, for now it is enough to know that everything between the { and } symbols is our program, or main
function.
Note that every statement you write is separated by ; (a semicolon), so one could write:
int main(void) { id myGreeter; myGreeter=[Greeter new]; [myGreeter greet]; [myGreeter free]; return EXIT_SUCCESS; } |
But that would make our code almost unreadable. So next to the ; separator, we also like to maintain readability. The ; separator is forced on us by the programming language, the line breaks and the idents are from me. You will soon learn that a lot of people have different conventions on how to layout code. I will not bother you with that. If you like to know more on that you could read GNU Coding standards.
Another agreement is to start with your variable declaration.
id myGreeter; |
Next we create our object.
myGreeter = [Greeter new]; |
Greeter
, and now we create a new instance by sending the new
message to Greeter
and we create the object myGreeter. The object is alive, we started it!Messages
By sending a message to an object, we ask the object to perform one of its methods. In this case we send to our object Greeter the message to perform the method
new
.new
is one of the methods Greeter inheritated from Object.Another way of creating an object is by using:
Which is often used in Cocoa and GNUstep programming. With this you see that you can send two messages after another to an object. Messages can be nested.
myGreeter = [[Greeter alloc] init];Our actress playing a role on stage, has to listen to the other players to know when and what to perform. The events happening on stage are the messages on which our actress acts.
Now that we have an operational object, we send another message to the object and that message asks the object to perform our method, namely greet
.
Now that the object has done what is should do it is time to let go. By sending the new
message we created the object which then of course occupies some memory space. By using free
we destroy the object so that the memory can be used again.
We end the function by returning a EXIT_SUCCESS value to indicate that all went well. In Objective-C this is noted as return EXIT_SUCCESS;
.
Exit status
In a lot of programs you might see
return 0;
when ending a program. On most systems a successful operation is indicated by 0 and a failure is reported as 1. However this is not the case on every system. For this the include file stdlib.h defines the values for success and failure through the names EXIT_SUCCESS and EXIT_FAILURE. Using these instead of 1 and 0 makes your code portable to every system.
You have seen that the a little program for just printing Hello, World! can give you a fair amount of text in you source file. To keep your programming organized and to help you in more effective reuse of your code it will help you to split the previous program in several parts.
The code of the program almost dictates where this sample should be split. The first section is the declaration of the object called the interface, the second part is the implementation of the object and the last part is the main function.
The first part we will be looking at is the interface of an object. The interface describes how our object is seen from the outside world and is described in a header file. Header files have the .h extension. In our Hello, World! example we created the Greeter object, create a file called Greeter.h with the following content:
/* Comments: ** Example written by Pascal Bourguignon ** Added comments in empty {} section by Chris B. Vetter */ #include <objc/Object.h> @interface Greeter:Object { /* This is left empty on purpose: ** Normally instance variables would be declared here, ** but these are not used in our example. */ } - (void)greet; @end |
The interface
The general definition of an interface looks like this
#include "ClassName.h" @interface ClassName ( CategoryName ) /* method declarations */ @endWhen using all kinds of small objects certain header-files like stdio.h might be included several times, which is not very economic. It would be nicer if it was only included once. To make sure that that is the case there is an construction that looks like this:
What this tells the compiler is that if _INCLUDED_STDIO is not set, set it and include stdio.h. The next time it comes across a #ifndef _INCLUDED_STDIO the variable is defined and stdio.h will not be included. You should of course use the same variable name for each ifndef.
#ifndef _INCLUDED_STDIO #define _INCLUDED_STDIO #include <stdio.h> #endif
We however need to implement our object Greeter by creating the class. To be able to reuse this object we create a single file that will only hold the objects implementation. So create a file called Greeter.m with the following content:
/* Comments: ** Example written by Pascal Bourguignon ** added #include "Greeter.h" after splitting hello.m by Dennis Leeuw */ #include <stdio.h> #include "Greeter.h" @implementation Greeter - (void)greet { printf("Hello, World!\n"); } @end |
It might get clear to you why I didn't group the #include statements in the single file hello.m program. The stdio.h is only needed by our object Greeter so it is included here, in the implementation. Also note the difference in include statements. And include statement embraced by < and > means that the .h file belongs to the system, while an inclusion with " and " means local to the directory where the Greeter.m file can be found.
The implementation
The general definition of an implementation looks like this
#include "CategoryName.h" @implementation ClassName ( CategoryName ) method definitions @end
And the last part of our program is the actual call to our object. This part is what makes it a program, the rest only defines our object. Here we create the main function, so let's call the file main.m and fill it with:
/* Comments: ** Example written by Pascal Bourguignon ** return status change by Chris B. Vetter ** int main() changed to int main(void) by Dennis Leeuw ** from comments by Chris B. Vetter ** added #include "Greeter.h" after splitting hello.m by Dennis Leeuw ** added #include <stdlib.h> to create system independance for return */ #include "Greeter.h" #include <stdlib.h> int main(void) { id greeter; greeter=[Greeter new]; [greeter greet]; [greeter free]; return EXIT_SUCCESS; } |
To compile this we have to do several things. First we need to create the object Greeter, then we need to create main and we then have to link them. That's quite a mouthful. Don't worry I am gonna explain it step by step for you and then tell you there is an easier way of doing things.
To translate the Greeter.m file into machine code run:
gcc -c Greeter.m |
The next stage is to do the same for main.m:
gcc -c main.m |
What we need to do to make this function is to link the two into a real program. We don't have to do this our selfs, we can just instruct gcc to do it for us:
gcc -lobjc -o Greeter main.o Greeter.o |
To avoid conflicts with hello, we created the executable with the name Greet, so type:
./Greet |
That was quite some work. Why the hell should we ever wanna do this? It was much easier when everything was one single file.
For such a small project like a Hello, World! program that might be the case, but for large projects with objects that you once wrote and might like to use in other applications it is a different subject. Not needing to copy and paste code from one file to anoter, but simply coping a .h and a .m file from one project directory to another is much easier.
While that part might have become easier the building process did not. And since programers are lazy people otherwise they wouldn't have been programmers, then they would have used paper and pencils, they created a solution called make. make is a tool that hence the name automates the build proces.
Create a Makefile, note the capital M, like this:
all: main.o Greeter.o gcc -lobjc -o Greet main.o Greeter.o main.o:main.m Greeter.h gcc -c main.m Greeter.o:Greeter.m Greeter.h gcc -c Greeter.m |
Makefiles
Makefiles have a very simple syntax:
From our example the first target you see is all. all is a make keyword, every Makefile needs a all target. Because running make without an argument assumes make all.
target: needs commandsBefore the command associated with all is run it needs main.o and Greeter.o. Luckily the Makefile also has those targets (you can name the targets anything you want, but this is the easiest to remember).
For every target it is checked if it needs are forfilled before the command is run. This is essentially what make does. This guide is not about Makefiles, so if you would like to more on this subject, read GNU Make
Save the file and type:
make |
As mentioned before Objective-C is an extension to the C programming language, which makes C object oriented. Then I told you that it wasn't something you wanted to know. I lied, you do want to know that. Without C there wouldn't be Objective-C. Objects in itself contain C code, so this section will introduce to you the basic parts of C, which happens to be the basic parts of Objective-C.
We will start with data and then look at functions, because that are the two elements that make up every computer language and an object.
There are two different kinds of programming languages. The first one is called statically typed and the other is called a dynamically typed language. The difference between the two has to do with how values are assigned to variables. A variable is a named container for a value. Like, VAR=1 means that the variable called VAR has the value 1. This assignment is dynamically, because we haven't told anybody that VAR actually contains an integer. This means that apperently VAR can hold any value. In a dynamically typed language VAR=a would also be legal.
However C is a statically typed language. Which means we have to tell upfront what kind of data a variable is going to hold. To do this the language provides us with predefined data types, with which we can initialize a variable. To e.g. tell a compiler that we use two integers in a program, one with the value 1 and the other with value 5, we do the following: Create a project directory called Numbers and put the following four files in it.
Integers.h:
/* Integers.h written by Dennis Leeuw */ #include <objc/Object.h> @interface Integers:Object { /* No instance variables */ } - (void)integers; @end |
/* Integers.m written by Dennis Leeuw */ #include <stdio.h> #include "Integers.h" @implementation Integers - (void)integers { int i; int j; i = i; j = 5; printf("I=%d, J=%d\n", i, j); } @end |
/* main.m written by Dennis Leeuw */ #include "Integers.h" #include <stdlib.h> int main(void) { id integer; integer=[Integers new]; [integer integers]; [integer free]; return EXIT_SUCCESS; } |
# Makefile written by Dennis Leeuw all: main.o Integers.o gcc -lobjc -o Numbers main.o Integers.o main.o: main.m Integers.h gcc -c main.m Integers.o: Integers.m Integers.h gcc -c Integers.m |
Run make. When you remove the line: int i, j; and then recompile the code, you get the warning: `i' undeclared (first use in this function) which exactly proves that C is a statically typed language. You need to declare your variables before you can work with them.
And since you need to type your variables, there are predefined types.
Data types
Data types tell the computer the kind of data that is represented, which actually means the amount of memory that needs to be reserved to store information. To give you an overview of data types and the memory that is needed:
Table 3-1. C standard types
Type Description Size char A character of the local character set 1 byte (8 bits) int An integer (whole numbers), e.g. 1, 2, 9, 1000 4 bytes (32 bits) float Floating point number, e.g. 1.2, 1.7, 5.8 4 bytes (32 bits) char. Is the basic unit within a Objective-C and C program. All others are derived from the size of char. char actually is a single character like 'a' or '3'. It's size is defined in limits.h, which should reside in /usr/include. In my limits.h file it says:
which means a char is 8 bits.
# define CHAR_BIT 8Table 3-2. Derivative types
Type Description Size short A short integer 2 bytes long A double short 4 bytes long long A double long 8 bytes double Double precision float 8 bytes long. Note that a long is equal to an int (? true ?)
Table 3-3. Objective-C data types
Type Description Size BOOL Boolean type which can either be YES or NO. NO is 0, YES is non-0. id An object 4 bytes STR IOD SEL IMP id. id is the data type that indicates that we are talking about an object. So to declare a variable greeter we proceed it by id. With which we tell that greeter is an object with its own rules.
All mentioned lengths are the most common ones for 32 bit machines, like the Intel Pentium and compatible chipsets. The actual length is machine dependend and can be found in the limits.h file and the float.h. Or can be shown with a little program like this:
Sizes.h:
Sizes.m:
/* Comments ** Example written by Dennis Leeuw */ #include <objc/Object.h> @interface Sizes:Object { /* This is left empty on purpose: ** Normally instance variables would be declared here, ** but these are not used in our example. */ } - (void)sizes; @endmain.m:
/* Comments: ** Example written by Dennis Leeuw ** C code by Pascal Bourguignon */ #include <stdio.h> #include "Sizes.h" @implementation Sizes - (void)sizes { printf("sizeof(char )=%4d\n",sizeof(char)); printf("sizeof(unsigned char )=%4d\n",sizeof(unsigned char)); printf("sizeof(short )=%4d\n",sizeof(short)); printf("sizeof(unsigned short )=%4d\n",sizeof(unsigned short)); printf("sizeof(int )=%4d\n",sizeof(int)); printf("sizeof(unsigned int )=%4d\n",sizeof(unsigned int)); printf("sizeof(long )=%4d\n",sizeof(long)); printf("sizeof(unsigned long )=%4d\n",sizeof(unsigned long)); printf("sizeof(long long )=%4d\n",sizeof(long long)); printf("sizeof(unsigned long long )=%4d\n",sizeof(unsigned long long)); printf("sizeof(float )=%4d\n",sizeof(float)); printf("sizeof(double )=%4d\n",sizeof(double)); printf("sizeof(void )=%4d\n",sizeof(void)); printf("sizeof(id )=%4d\n",sizeof(id)); } @endMakefile:
/* Comments: ** Example written by Dennis Leeuw */ #include "Sizes.h" #include <stdlib.h> int main(void) { id sizelist; sizelist=[Sizes new]; [sizelist sizes]; [sizelist free]; return EXIT_SUCCESS; }
all: main.o Sizes.o gcc -lobjc -o Sizelist main.o Sizes.o main.o: main.m Sizes.h gcc -c main.m Sizes.o: Sizes.m Sizes.h gcc -c Sizes.mThe output this program returns is the relative size to the size of char. So if char is 8 bits and short int is 16, it will mention short =2. Since the ANSI C standard specifies sizeof(char) is equal to 1 by definition and sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long). But I think we are already to far away from the path we want to follow...
You might know that computers only use a 1 and a 0 for their computations. That's why they call it a binary, from bi: 2. So everything we write in a program is actually converted to 1s and 0s. If we say that a integer is defined in a program, we actually mean that a piece of memory is reserved to store the amount of data that is asociated with an integer.
So let's have a look again at our Numbers program. We defined i and j to be short integers and from the above table we can read that that means that we use 32 bits of memory space for these values. So if we write our decimal numbers 1 and 5 into binary format that would mean:
Table 3-4. Decimal to binary
Decimal | Binary |
---|---|
1 | 00000000000000000000000000000001 |
5 | 00000000000000000000000000000101 |
With those 32 bits we can represent numbers ranging from 0 to 429496729. This is what is called a unsigned int.
Now change the int to short int, and you will see that it still works. What we just did, is called optimization. Since we know that i, and j, will always be 1 and 5, there is no need for them to occupie 32 bits of memory. Those values can be stored in 16 bits, so that is what we did. We told the compiler that i and j are both short integers, and saved 2 times 16 equals 32 bits of memory space.
Up until now we have worked with characters and integers, that both always represent whole numbers. If we however assign 0.1 to i. The compiler doesn't complain, but when we look at the output as soon as we run the number command. You will see that i now has a value of 0.
This is not what we want. Change the Integer.m file to look like this, and mark the changed prinf statement!:
/* Example written by Dennis Leeuw */ #include <stdio.h> #include "Integers.h" @implementation Integers - (void)integers { float i; short int j; i = 0.1; j = 5; printf("I=%f, J=%d\n", i, j); } @end |
The output of number should look like I=0.100000, J=5. i is a floating point number, but what does this mean for our memory consumption? From the table we know that we now use 32 bits of memory space.
We might also want to represent negative numbers, e.g. -1. Edit the program, change the i = 0.1; to i = -1;, make i a short int and recompile. No errors where produced, so somehow the computer new it was a negative number and could work with it. How is that done?
The agreement is that negative numbers are representations of the normal number, with all bits negated and then 1 added. Which means that -1 is written as:
Table 3-5. Creating -1
1: | 0000000000000001 |
negation: | 1111111111111110 |
add 1: | 0000000000000001 |
-1: | 1111111111111111 |
If we do the same for -5. That would look like this:
Table 3-6. Creating -5
5: | 0000000000000101 |
negation: | 1111111111111010 |
add 1: | 0000000000000001 |
-5: | 1111111111111011 |
What actually happens is that the first bit, when read from left to right, is the sign bit and tells our computer that we are working with negative numbers. The effect is that we now can address numbers in the range from -32768 to 32767 (since we are using short ints that normaly range between 0 and 65535), and the typing in the program should have been signed short int. Due to the intelligence in the gcc compiler, the problem was solved and no warnings where produced.
In our first numbers program we unknowingly used a prefix before our data type declaration of 'unsigned' (meaning whole positive numbers). As we have discovered now there are two types of prefixes: unsigned and signed.
For testing sake, change Integers.m to the following and compile:
/* Example written by Dennis Leeuw */ #include <stdio.h> #include "Integers.h" @implementation Integers - (void)integers { unsigned short int i; unsigned int j; i = -1; j = 5; printf("I=%d, J=%d\n", i, j) } @end |
Notice that we declared i as an unsigned int, which means we can not use negative numbers, but assign -1 to i. The compiler will not complain about this, but when we do run the program, the output might suprise us: I=65535, J=5. Ofcourse this is not correct, but I wanted to show you how one can easily shoot oneself in the foot.
Now that we have seen the different data types, it might be useful to do something with them. After all the thing you are working on is called a computer so lets compute!
Operators
Table 3-7. Arithmetic operators
Operator Function + Addition - Substraction / Division * Multiplication % Modulus Table 3-8. Relational operators
Operator Function > Greater then >= Greater then or equal to == Equal to != Not equal to <= Less then or equal to < Less then The relational and logical operators together form the Boolean operators since their result is always TRUE or FALSE.
Change Integers.m to look this:
/* Integers.m written by Dennis Leeuw */ #include <stdio.h> #include "Integers.h" @implementation Integers - (void)integers { unsigned short int i; unsigned short int j; unsigned short int s; i = i; j = 5; s = i + j; printf("%d + %d = %d\n", i, j, s); } @end |
Let's try division, by replacing the - sign by the / sign. Now the outcome is 1 / 5 = 0. Which is correct when talking about integers but not in real life, so let's make s a float, and replace the last %d in the prinf statement by %f. Still the outcome is 0.000000, because the i and j are integers, so what we need to do is something that is called typecasting.
Replace s = i / j; by s = (float) i / j;. This means that our integer i is first converted to a float, then the division is performed, and the outcome assigned to s.
Typecasting
Typecasting allows you to do on the fly type conversions. Typecasting is done by using the needed type in parentheses, (), and putting it in front of the value or variable you want to change.
I feel I am getting the hang of it. It almost seems easy. Let's do something more complex.
Up until now we have worked with single entities asignments. To take a char example: char a; a='A';. But what if you want to assign a whole word, or even a sentence?
More then single byte chars do not exist, but arrays do.
Array
An array is a named container that can hold several identical data types. Like an array of integers, an array of characters (also called a string), an array of floats, etc.
An array is defined by first giving the type, then the name and between [] the amount of elements that the array can hold.
Numbers start from 0, so the first element in a is a[0], followed by a[1], a[2], a[3] and ending by a[9].
int a[10]; /* Array of integers that can hold 10 integer values */ char b[20]; /* Array of chars that can hold 20 characters */ float c[5]; /* Array of floats that can hold 5 floating point values */
A little experimenting with arrays, resulted in the following example:
/* Integers.m ** Example written by Dennis Leeuw */ #include <stdio.h> #include "Integers.h" @implementation Integers - (void)integers { char w[4]; w[0]='H'; w[1]='e'; w[2]='l'; w[3]='o'; printf("%c%c%c%c%c\n", w[0],w[1],w[2],w[2],w[3]); } @end |
First of all put your characters between ''. If you don't the compiler will treat them as variables, and says they are undeclared.
The thing is that this will print a nice Hello to your screen, and it illustrates the use of arrays on characters, although I must admit that a simple printf("Hello"); would have been easier.
FIXME: Complex arrays... box...
The idea is clear I hope, an array is a list of elements of the same type. The next data element might not come as an surprise. We are going to create a data element that is a list of different data types within one structure, hence the name structure.
TODO: Structure, Pointer
Next to data and data-types, the basic routines to manipulate data are functions. Every object within an object oriented language consists of data and functions, and to get a better understanding of that I will now introduce you to functions.
Within C programming a function definition looks like this:
return-type |
As mentioned before the function-name can be anything as long as it is not main
. You will hopefully also note that a function has a return-type and might have parameters.
Every function should return something when it is finished. It might be that it tells that it finished okay, like our return 0; statement, or it can return anything else. You might have noticed that all our examples main
functions had a return-type of int, which corresponds to the return 0;.
Other values are ofcourse possible, and they must be the same as one of the data-types we discussed. Since we can only return things that do exist, don't we. So the table of the different return-types is identical to the list of data-types.
For the parameter declaration more or less the same is true. The parameter declaration describes the data the function expects. In our case all main
are defined as void, which means they don't expect any input (for non-english speakers: void means something like empty).
FIXME: In the main function we tell thus that the return-type of the function is an int and that there are no parameter declarations (this means the function does not accept any arguments, you can not send data to this function), since it says void . Then there is the compound statement {} which tells that everything that follows belongs to the function named main
.
As we have seen in our Hello, World! example a function might come from an external library. We will soon learn that most functions will come from external sources since C and thus Objective-C is in it self a very small and compact language. Only the bare minimum is available. I will use this section to get you familiar with that bare minimum.
Change Integers.m to the following:
/* Comments: ** Example written by Dennis Leeuw */ #include <stdio.h> #include "Integers.h" @implementation Integers - (void)integers { short int i; short int j; float s; i = 1; j = 5; s = (float) i / j; if (s < 0) printf("s=%f is less then 0\n", s); else if (s == 0) printf("s=%f is equal to 0\n", s); else printf("s=%f is greater then 0\n", s); } @end |
Change the s = (float) i / j; to s = i - j and you will see that s is less then 0. Now you can fool around with this if you like. I know I did.
if ... else
For testing on boolean trueness there is the if ... else construction. The general form is:
Where the else construction might be omitted.
if (test) do_something else do_something_elseThe logic behind this is that if the test between parenthesis is true the do_something is executed, while if it is not true the do_something is skipped. Complex tests can be created by using else if constructions.
The do_something or do_something_else can only be single statements. While test can be a complex boolean expression with by parenthesis grouped tests, like:
if ((x<y) && (a==b) || (k<=m)) printf("That's complex");
If we can test on things being true or false, we can create loops. Let's investigate. I found out there are three different ways of creating loops. There is while, there is do and there is for. I'll give you examples of while and for, since the do-loop is just a while-loop. We start with while:
/* Integers.m ** Example written by Dennis Leeuw */ #include <stdio.h> #include "Integers.h" @implementation Integers - (void)integers { short int i; short int j; short int s; i = 1; j = 5; s = i - j; while ( s < j ) { printf("s=%d and j=%d\n", s, j); s = s + 1; } } @end |
while
The abstract definition of a while loop looks like this:
which can be read as while test is true do statement. So as long as the boolean test is true statement is executed. If you replace test by 1, which makes the while test always true, you have an endless loop.
while (test) { statement; }Note the block symbols {} that group the statements. If you only have a single statement you can ofcourse leave out the block symbols.
The nice thing about prgrammers, to me, is that they are lazy people. If things can be made easier (less typing) they will do so. The line s = s + 1; is an example of too much typing. The short hand notation would be s++;
Table 3-10. More short hand notations
Normal | Short |
---|---|
s = s + 1; | s++; |
s = s - 1; | s--; |
s = s + 5; | s += 5; |
s = s * j; | s *= j; |
Rewriting the above while-loop into a for-loop looks like this:
/* Integers.m ** Example written by Dennis Leeuw */ #include <stdio.h> #include "Integers.h" @implementation Integers - (void)integers { short int i; short int j; short int s; for ( i=1,j=5,s=i-j; s<j; printf("s=%d and j=%d\n", s, j),s++ ); } @end |
Let me start by creating a more readable format for the for loop:
for ( i=1, j=5, s=i-j; s<j; printf("s=%d and j=%d\n", s, j), s++ ); |
TODO: break/continue, switch, goto
This chapter was actually a very short C course, wrapped in Objective-C. Almost all examples could have been plain C programs. It illustrates the closeness of Objective-C and C. With what I have written (and learned myself) we hopefully have enough basis to learn more about Objective-C. As said in the introduction this guide will not master you in Objective-C, just gets you started.
But it should feel nice that a simple C course can fit into a single chapter. The downside of all this is that because C and thus Objective-C is such a simple language you need to do a lot yourself, or use someone elses code.
There are a lot of good C courses, and very fine books on C programming. If you like to know more about C I would like to point you to atleast the Learning GNU C book. We will continue our journey to get familiar with Objective-C and especially GNU Objective-C since that is what is provided by the GNU Compiler Collection.
The above chapters have given you an almost complete description of the Objective-C language. If you need more (and trust me you do), you need to use code from others. That might be provided through the C-library or Objective-C runtime on your system, or it might be libraries, functions or objects from others.
Most standard C functions are explained well in other documents, but there is also a lot of documentation already available on your system through the manual-pages and the header-files.