Difference between revisions of "Objective-C Variable Scope and Storage Class"
(→Function Scope) |
|||
Line 88: | Line 88: | ||
Objective-C code is typically structured into a number of classes and code units called ''functions'' (for more details on functions see the chapter entitled [[An Overview of Objective-C Functions]]). In terms of variable scope, functions are really little more than statement blocks in that they are encapsulated in braces and variables declared within those braces are local to that block. | Objective-C code is typically structured into a number of classes and code units called ''functions'' (for more details on functions see the chapter entitled [[An Overview of Objective-C Functions]]). In terms of variable scope, functions are really little more than statement blocks in that they are encapsulated in braces and variables declared within those braces are local to that block. | ||
+ | |||
+ | In the following example is intended to illustrate this concept and contains two functions named ''main()'' and ''multiply()'' respectively. | ||
+ | |||
+ | <pre> | ||
+ | #import <Foundation/Foundation.h> | ||
+ | |||
+ | int main (int argc, const char * argv[]) | ||
+ | { | ||
+ | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | ||
+ | |||
+ | |||
+ | int j = 10; | ||
+ | int k = 20; | ||
+ | int result; | ||
+ | |||
+ | result = mutliply(); | ||
+ | |||
+ | [pool drain]; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | int multiply() | ||
+ | { | ||
+ | return j * k; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | An attempt to compile the above code example will result in a compiler error similar to the following: | ||
+ | |||
+ | <pre> | ||
+ | In function 'multiply': | ||
+ | error: 'j' undeclared (first use in this function) | ||
+ | error: (Each undeclared identifier is reported only once | ||
+ | error: for each function it appears in.) | ||
+ | error: 'k' undeclared (first use in this function) | ||
+ | </pre> | ||
+ | |||
+ | The reason for this error is that variables ''j'' and ''k'' are declared in the ''main()'' function and are, therefore, local to that function. As such, these variables are not visible to the ''multiply()'' function resulting in an error when we try to reference them in that function. If we wanted to have access to the values of those variables we would have to pass them through as arguments to the multiply function (for details on function arguments refer to [[An Overview of Objective-C Functions]]). | ||
+ | |||
+ | As with block scope, it is possible to have multiple variables with the same inside a single function as long as each instance appears within its one local scope. For example, we can add a while loop to our ''main()'' that has its own local of variable also named ''j'': | ||
+ | |||
+ | <pre> | ||
+ | int main (int argc, const char * argv[]) | ||
+ | { | ||
+ | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | ||
+ | |||
+ | |||
+ | int j = 10; | ||
+ | int k = 20; | ||
+ | int result; | ||
+ | |||
+ | while (k > 0) | ||
+ | { | ||
+ | int j = 0; | ||
+ | j =+ k; | ||
+ | k--; | ||
+ | } | ||
+ | |||
+ | [pool drain]; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | == Global Scope == | ||
+ | |||
+ | A variable that has ''global scope'' is potentially accessible to code anywhere else in an Objective-C program, regardless of whether the code is in a separate file to the one in which the variable is declared. Global variables are declared ''outside'' of any statement blocks and are typically placed near the top of a source file. | ||
+ | |||
+ | The following is a code listing from an Objective-C source file named ''main.m'': | ||
+ | |||
+ | <pre> | ||
+ | #import <Foundation/Foundation.h> | ||
+ | |||
+ | int myVar = 321; | ||
+ | |||
+ | int main (int argc, const char * argv[]) | ||
+ | { | ||
+ | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | ||
+ | |||
+ | NSLog (@"myVar = %i", myVar); | ||
+ | [pool drain]; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | As we can see, the global variable ''myVar'' is declared outside of the ''main()'' function is is not embedded into a statement block or class declaration of any kind. Because this is a global variable, it is directly accessible inside the main function and would also be accessible from any other functions and statement blocks within the context of the ''main.m'' code file. | ||
+ | |||
+ | As previously stated, global variables are potentially accessible across all the source files that constitute an Objective-C program. The key word here is ''potentially'' and we say this because global access across multiple source files is not the default for a global variable. Take, for example, the following two code files. The first file, ''main.m'' contains the declaration of the global variable ''myVar'' and calls a function named ''displayit()'': | ||
+ | |||
+ | <pre> | ||
+ | #import <Foundation/Foundation.h> | ||
+ | |||
+ | int myVar = 321; | ||
+ | |||
+ | int main (int argc, const char * argv[]) | ||
+ | { | ||
+ | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | ||
+ | |||
+ | NSLog (@"myVar = %i", myVar); | ||
+ | |||
+ | displayit(); | ||
+ | [pool drain]; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | The second file, named ''displayit.m'' contains the code for the ''displayit()'' function and displays the value currently assigned to the global ''myVar'' variable: | ||
+ | |||
+ | <pre> | ||
+ | void displayit() | ||
+ | { | ||
+ | NSLog (@"MyVar from different source file is %i", myVar); | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | If we try to compile these two code files into an executable either using Xcode or from the command-line we will get an error that reads as follows: | ||
+ | |||
+ | <pre> | ||
+ | gcc -framework Foundation main.m displayit.m -o main | ||
+ | displayit.m: In function 'displayit': | ||
+ | displayit.m:3: error: 'myVar' undeclared (first use in this function) | ||
+ | displayit.m:3: error: (Each undeclared identifier is reported only once | ||
+ | displayit.m:3: error: for each function it appears in.) | ||
+ | </pre> | ||
+ | |||
+ | The reason for this is that although the variable was declared as global in ''main.m'' we still need to take one extra step in ''displayit.m'' to make the variable accessible in this file. This step involves declaring that the variable is ''external'' to the local file. This declaration is achieved using the ''extern'' keyword. For example, to make ''myVar'' accessible in ''displayit.m'' the following code would be required: | ||
+ | |||
+ | <pre> | ||
+ | extern int myVar; | ||
+ | |||
+ | void displayit() | ||
+ | { | ||
+ | NSLog (@"MyVar from different source file is %i", myVar); | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | Having made this change, the code will now compile and run. | ||
+ | |||
+ | == File Scope == | ||
+ | |||
+ | In the preceding section on ''global scope'' we talked about how a variable declared outside of any statement blocks is considered to be global and may be access both by code in the same file, or by code in different files. Suppose, however, that you wanted a variable to accessible ''only'' to code within the file where the variable is declared. This is achieved by using the ''static'' specifier when declaring the variable. For example, the following code is from a file named ''main.m''. This file declares variable ''myVar'' to be static. As such, the variable will be accessible only to code within the ''main.m'' file and cannot be accessed by code in any other file: | ||
+ | |||
+ | <pre> | ||
+ | #import <Foundation/Foundation.h> | ||
+ | |||
+ | static int myVar = 543; | ||
+ | |||
+ | int main (int argc, const char * argv[]) | ||
+ | { | ||
+ | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | ||
+ | |||
+ | NSLog (@"myVar = %i", myVar); | ||
+ | |||
+ | displayit(); | ||
+ | [pool drain]; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | == Variable Storage Class == | ||
+ | |||
+ | Variable storage class specifiers are used when declaring a variable to give the compiler information about how a variable is likely to be used and accessed within the program being compiled. So far in this chapter we have actually already looked at two storage class specifiers in the form of ''extern'' and ''static''. A full list of variable storage class specifiers is as follows: | ||
+ | |||
+ | * '''extern''' - Specifies that the variable name is referencing a global variable specified in a different source file to the current file. | ||
+ | |||
+ | * '''static''' - Specifies that the variable is to be accessible only within the scope of the current source file. | ||
+ | |||
+ | * '''auto''' - The default value for variable declarations. Specifies the variable is to be local or global depending on where the declaration is made within the code. Since this is the default setting this specifier is rarely, if ever, used. | ||
+ | |||
+ | * '''const''' - Declares a variable as being read-only. In other words, specifies that once the variable has been assigned a value, that value will not be subsequently changed. | ||
+ | |||
+ | * '''volatile''' - Specifies that the value assigned to a variable will be changed in subsequent code. The default behavior for variable declarations. |
Revision as of 15:01, 20 October 2009
In previous chapters we have invested a considerable amount of time talking about variables in terms of the various types available and how they are defined. One area we have yet to cover involves the places from which a variable is accessible once it has been declared. This is a concept known as variable scope and the scope of a variable is very much dependent upon where it is declared within Objective-C code.
In this chapter we will also look at the Objective-C variable storage classes. These are specifiers that tell the Objective-C compiler information about how the variable is going to be used within the application code.
Variable Scope
An Objective-C program will consist of code divided up into functions, classes and code structures (such as do .. while and for loops). Invariably a typical program will make extensive use of variables to store and manipulate data. Once a variable has been declared it may or may not be accessible to other sections of the program code. This accessibility depends on where and how the variable was declared and where the code is that needs to access it. This is known as variable scope and falls into a number of categories, each of which will be covered in this chapter.
Block Scope
Objective-C code is divided into sequences of code blocks and files that define the structure of a program. Each block, referred to as a statement block is encapsulated by braces ({}). For example, a for loop typically contains a statement block:
int x; for (x = 0; x < 10; x++) { int j = x + 10; NSLog (@"j = %i", j); }
In the above example, variable j is declared within the statement block of the for loop and as such is considered to be local to that block. This means that the variable can be accessed from within the for loop statement block but is essentially invisible and inaccessible to code anywhere else in the program code. Any attempt to access variable j outside the for loop will result in a compile error. For example:
int x; for (x = 0; x < 10; x++) { int j = x + 10; NSLog (@"j = %i", j); } j += 20; // illegal - j is now out of scope
An attempt to compile the above code within the context of an application will result in a compilation error along the lines of the following:
error: 'j' undeclared (first use in this function)
An interesting side effect of variable scope within this context is that it enables us to have more than one variable with the same name as long as they are in different scopes. For example, we can have a variable named j declared outside the for loop and a variable named j declared inside the for loop. Although these variables have the same name they occupy different memory locations and contain different values. This can be illustrated using a simple application that contains two jj variables:
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int x; int j = 54321; for (x = 0; x < 10; x++) { int j = x + 10; NSLog (@"Varible j in for loop is %i", j); } NSLog (@"Variable j outside for loop is %i", j); [pool drain]; return 0; }
As we can see from the above code, variable j is declared twice but in two different scopes. When compiled and executed we can see that modifying the value of jj within the for loop has no effect on the value assigned to the variable jj declared outside the for loop:
2009-10-19 15:09:39.875 t[8130:10b] Varible j in for loop is 10 2009-10-19 15:09:39.877 t[8130:10b] Varible j in for loop is 11 2009-10-19 15:09:39.877 t[8130:10b] Varible j in for loop is 12 2009-10-19 15:09:39.878 t[8130:10b] Varible j in for loop is 13 2009-10-19 15:09:39.878 t[8130:10b] Varible j in for loop is 14 2009-10-19 15:09:39.879 t[8130:10b] Varible j in for loop is 15 2009-10-19 15:09:39.879 t[8130:10b] Varible j in for loop is 16 2009-10-19 15:09:39.879 t[8130:10b] Varible j in for loop is 17 2009-10-19 15:09:39.880 t[8130:10b] Varible j in for loop is 18 2009-10-19 15:09:39.880 t[8130:10b] Varible j in for loop is 19 2009-10-19 15:09:39.881 t[8130:10b] Variable j outside for loop is 54321
Function Scope
Objective-C code is typically structured into a number of classes and code units called functions (for more details on functions see the chapter entitled An Overview of Objective-C Functions). In terms of variable scope, functions are really little more than statement blocks in that they are encapsulated in braces and variables declared within those braces are local to that block.
In the following example is intended to illustrate this concept and contains two functions named main() and multiply() respectively.
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int j = 10; int k = 20; int result; result = mutliply(); [pool drain]; return 0; } int multiply() { return j * k; }
An attempt to compile the above code example will result in a compiler error similar to the following:
In function 'multiply': error: 'j' undeclared (first use in this function) error: (Each undeclared identifier is reported only once error: for each function it appears in.) error: 'k' undeclared (first use in this function)
The reason for this error is that variables j and k are declared in the main() function and are, therefore, local to that function. As such, these variables are not visible to the multiply() function resulting in an error when we try to reference them in that function. If we wanted to have access to the values of those variables we would have to pass them through as arguments to the multiply function (for details on function arguments refer to An Overview of Objective-C Functions).
As with block scope, it is possible to have multiple variables with the same inside a single function as long as each instance appears within its one local scope. For example, we can add a while loop to our main() that has its own local of variable also named j:
int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int j = 10; int k = 20; int result; while (k > 0) { int j = 0; j =+ k; k--; } [pool drain]; return 0; }
Global Scope
A variable that has global scope is potentially accessible to code anywhere else in an Objective-C program, regardless of whether the code is in a separate file to the one in which the variable is declared. Global variables are declared outside of any statement blocks and are typically placed near the top of a source file.
The following is a code listing from an Objective-C source file named main.m:
#import <Foundation/Foundation.h> int myVar = 321; int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog (@"myVar = %i", myVar); [pool drain]; return 0; }
As we can see, the global variable myVar is declared outside of the main() function is is not embedded into a statement block or class declaration of any kind. Because this is a global variable, it is directly accessible inside the main function and would also be accessible from any other functions and statement blocks within the context of the main.m code file.
As previously stated, global variables are potentially accessible across all the source files that constitute an Objective-C program. The key word here is potentially and we say this because global access across multiple source files is not the default for a global variable. Take, for example, the following two code files. The first file, main.m contains the declaration of the global variable myVar and calls a function named displayit():
#import <Foundation/Foundation.h> int myVar = 321; int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog (@"myVar = %i", myVar); displayit(); [pool drain]; return 0; }
The second file, named displayit.m contains the code for the displayit() function and displays the value currently assigned to the global myVar variable:
void displayit() { NSLog (@"MyVar from different source file is %i", myVar); }
If we try to compile these two code files into an executable either using Xcode or from the command-line we will get an error that reads as follows:
gcc -framework Foundation main.m displayit.m -o main displayit.m: In function 'displayit': displayit.m:3: error: 'myVar' undeclared (first use in this function) displayit.m:3: error: (Each undeclared identifier is reported only once displayit.m:3: error: for each function it appears in.)
The reason for this is that although the variable was declared as global in main.m we still need to take one extra step in displayit.m to make the variable accessible in this file. This step involves declaring that the variable is external to the local file. This declaration is achieved using the extern keyword. For example, to make myVar accessible in displayit.m the following code would be required:
extern int myVar; void displayit() { NSLog (@"MyVar from different source file is %i", myVar); }
Having made this change, the code will now compile and run.
File Scope
In the preceding section on global scope we talked about how a variable declared outside of any statement blocks is considered to be global and may be access both by code in the same file, or by code in different files. Suppose, however, that you wanted a variable to accessible only to code within the file where the variable is declared. This is achieved by using the static specifier when declaring the variable. For example, the following code is from a file named main.m. This file declares variable myVar to be static. As such, the variable will be accessible only to code within the main.m file and cannot be accessed by code in any other file:
#import <Foundation/Foundation.h> static int myVar = 543; int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog (@"myVar = %i", myVar); displayit(); [pool drain]; return 0; }
Variable Storage Class
Variable storage class specifiers are used when declaring a variable to give the compiler information about how a variable is likely to be used and accessed within the program being compiled. So far in this chapter we have actually already looked at two storage class specifiers in the form of extern and static. A full list of variable storage class specifiers is as follows:
- extern - Specifies that the variable name is referencing a global variable specified in a different source file to the current file.
- static - Specifies that the variable is to be accessible only within the scope of the current source file.
- auto - The default value for variable declarations. Specifies the variable is to be local or global depending on where the declaration is made within the code. Since this is the default setting this specifier is rarely, if ever, used.
- const - Declares a variable as being read-only. In other words, specifies that once the variable has been assigned a value, that value will not be subsequently changed.
- volatile - Specifies that the value assigned to a variable will be changed in subsequent code. The default behavior for variable declarations.