An Overview of Objective-C Functions
Previous | Table of Contents | Next |
Objective-C Variable Scope and Storage Class | Objective-C Enumerators |
Purchase the full edition of this Objective-C book in Print ($14.99) or eBook ($12.99) format Objective-C 2.0 Essentials Print and eBook (ePub/PDF/Kindle) editions contain 31 chapters. |
Functions are a vital part of writing well structured and efficient code. Objective-C functions provide a way to organize programs and avoid code repetition. In this chapter of Objective-C 2.0 Essentials we will look at how functions are declared and used.
What is a Function?
A function is a named block of code that can be called upon to perform a specific task. It can be provided data on which to perform the task and is capable of returning a result to the code that called it. For example, if a particular arithmetic calculation needs to be performed in an Objective-C program the code to perform the arithmetic can be placed in a function. The function can be programmed to accept the values on which the arithmetic is to be performed (referred to as arguments) and to return the result of the calculation. At any point in the program code where the calculation is required, the function is simply called and the result returned.
How to Declare an Objective-C Function
<return type> <function name> (<arg1 type> <arg1 name>, <arg2 type> <arg2 name>, ... )
{
// Function code
}
Explanations of the various fields of the function declaration are as follows:
- <return type> - Specifies the data type of the result returned by the function. If the function does not return a result then void should be specified. Unless otherwise specified, functions are assumed to return an int.
- <function name> - The name assigned to the function. This is the name by which the function will be referenced when it is called from within the application code. Note that, unless otherwise specified using the static specifier, function names are global and must be unique within the context of an application to avoid compilation errors.
- <argn type> - The type of the argument passed through to the function.
- <argn name> - The name by which the argument is to be referenced in the function code.
- Function code - The code of the function that does the work.
As an example, the following function takes no arguments, returns no result and simply displays a message:
void sayhello () { NSLog (@"Hello"); }
The following sample function, on the other hand, takes two integer arguments and returns the result of a multiplication of those numbers:
int multiply (int x, int y) { return x * y; }
The main() Function
If you have been reading this book sequentially, you will have noticed that many of the examples in previous chapters have contained a function called main. This is a special function name that tells the Objective-C compiler where to start program execution. If you do not have a main function your code will fail during the link phase of the build process. The syntax for a main function is as follows:
int main (int argc, const char * argv[]) { @autoreleasepool { // Code here } return 0; }
<google>IOSBOX</google> The argc argument contains a count of the number of arguments that were found on the command line when the program was executed, and argv is a pointer to an array containing those arguments.
Calling an Objective-C Function
Once a function is declared it can be called from anywhere in the code, regardless of whether the code is in the same source file as the declaration or in a different file. This is because functions are global by default. After the different source files have been compiled to object code, a tool called the linker then performs the task of resolving undefined symbols in each object file. When it finds a call to an undefined function in one object file it searches the other object files that comprise the code and any libraries specified until it finds it. If no match is found the linker will fail with an undefined symbol error.
Functions are called using the following syntax:
<function name> (<arg1>, <arg2>, ... )
For example, to call a function named sayhello that takes no arguments and returns no value, we would write the following code:
sayhello();
To call a function called multiply that take two arguments and returns an integer we might write the following code:
int result; result = multiply (10, 20);
In the above example, we have created a new variable called result and then used the assignment operator (=) to store the result returned by the multiply function when it is passed the numbers 10 and 20 as arguments.
Function Prototypes
Where a function is declared in relation to where it is called from is a significant factor. In the same way that most cultures read a page from top to bottom, a compiler reads an Objective-C source file from top to bottom. If the compiler comes across a call to a function before it has found the function declaration it has to make assumptions about the type of result that function returns. The default assumption is that the function will return an int. Having made this assumption, if when the compiler finally reaches the function declaration, an error will have to reported if it is found to return a data type other than an int. To see this in action, try compiling the following code:
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { @autoreleasepool { float result; result = multiply( 10, 20 ); } return 0; } float multiply (int x, int y) { return x * y; }
When an attempt to compile the above code is made, the compilation will fail with the following message:
warning: implicit declaration of function 'multiply' is invalid in C99 [-Wimplicit-function-declaration] error: conflicting types for 'multiply' main.m:8:18:note: previous implicit declaration is here
The compiler is complaining because it had assumed when the function was called at line 10 that it returned an int because up until that point it had not found a function declaration to tell it otherwise. Having made this assumption it then found the function declaration and discovered it actually returned a float, thereby causing a conflict.
There are two solutions to this problem. One is to always declare function before they are called:
#import <Foundation/Foundation.h> float multiply (int x, int y) { return x * y; } int main (int argc, const char * argv[]) { @autoreleasepool { float result; result = multiply( 10, 20 ); } return 0; }
This is a work around for simple cases but can quickly become unmanageable in larger application code bases with multiple files and functions calling other functions. A much better solution is to use a function prototype. This is a declaration that can be placed at the top of code files that pre-declares the return type and arguments of a function. For example, the function prototype for our multiply function is as follows:
float multiply (int x, int y);
The argument names in the function prototype are entirely optional and the same result can be achieved with just the argument types:
float multiply (int, int);
Note that the semi-colon (;) is mandatory.
We can now apply this concept to our earlier example:
#import <Foundation/Foundation.h> float multiply (int, int); int main (int argc, const char * argv[]) { @autoreleasepool { float result; result = multiply( 10, 20 ); } return 0; } float multiply (int x, int y) { return x * y; }
For functions that accept a variable number of arguments, the '...' directive can be used in the function prototype:
int addAll (int, ...);
Function Scope and the static Specifier
Objective-C functions are considered by default to be global in scope. This means that a function declared in one file in a program can be called from any other code file in the program. This means that function names must be unique. Two functions with the same name will cause an error when the code is linked during the build process. To confine the scope of a function to the file in which it is declared, simply prefix the declaration with the static keyword:
static float multiply (int x, int y) { return x * y; }
Static Variables in Functions
In the normal course of program execution, any variables declared locally in a function are discarded when the function exits and execution returns to the code location from where the call was made. For example, each time the displayit function is called in the following code, variable y is re-initialized to 0:
#import <Foundation/Foundation.h> void displayit (int); int main (int argc, const char * argv[]) { @autoreleasepool { int i; for (i=0; i<5; i++) { displayit( i ); } } return 0; } void displayit (int i) { int y = 0; y += i; NSLog (@"y + i = %i", y); }
When executed we get the following output from the above code:
2009-10-20 15:39:12.306 t[10128:10b] y + i = 0 2009-10-20 15:39:12.308 t[10128:10b] y + i = 1 2009-10-20 15:39:12.308 t[10128:10b] y + i = 2 2009-10-20 15:39:12.309 t[10128:10b] y + i = 3 2009-10-20 15:39:12.309 t[10128:10b] y + i = 4
If we want the value of y to be retained after the function has executed, we simply declare the variable as static:
static int y = 0;
Now, when the code is compiled and run, we get the following output because the value of y is not being reset to 0 each time the function is called:
2009-10-20 15:39:32.194 t[10136:10b] y + i = 0 2009-10-20 15:39:32.195 t[10136:10b] y + i = 1 2009-10-20 15:39:32.195 t[10136:10b] y + i = 3 2009-10-20 15:39:32.196 t[10136:10b] y + i = 6 2009-10-20 15:39:32.196 t[10136:10b] y + i = 10
Purchase the full edition of this Objective-C book in Print ($14.99) or eBook ($12.99) format Objective-C 2.0 Essentials Print and eBook (ePub/PDF/Kindle) editions contain 31 chapters. |
Previous | Table of Contents | Next |
Objective-C Variable Scope and Storage Class | Objective-C Enumerators |