Objective-C Variable Scope and Storage Class

PreviousTable of ContentsNext
Objective-C Dynamic Binding and Typing with the id TypeAn Overview of Objective-C Functions

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.

Buy Print

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 the Objective-C code of a program.

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: use of undeclared identifier 'j'

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 j variables:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        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);

    }
    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 j within the for loop has no effect on the value assigned to the variable j declared outside the for loop:

2009-10-19 15:09:39.875 t[8130:10b] Variable j in for loop is 10
2009-10-19 15:09:39.877 t[8130:10b] Variable j in for loop is 11
2009-10-19 15:09:39.877 t[8130:10b] Variable j in for loop is 12
2009-10-19 15:09:39.878 t[8130:10b] Variable j in for loop is 13
2009-10-19 15:09:39.878 t[8130:10b] Variable j in for loop is 14
2009-10-19 15:09:39.879 t[8130:10b] Variable j in for loop is 15
2009-10-19 15:09:39.879 t[8130:10b] Variable j in for loop is 16
2009-10-19 15:09:39.879 t[8130:10b] Variable j in for loop is 17
2009-10-19 15:09:39.880 t[8130:10b] Variable j in for loop is 18
2009-10-19 15:09:39.880 t[8130:10b] Variable 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 function block.

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[])
{
   @autoreleasepool {
        int j = 10;
        int k = 20;
        int result;

        result = mutliply();

   }
   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. 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) or specify them as global or static variables (discussed below).

As with block scope, it is possible to have multiple variables with the same name inside a single function as long as each instance appears within its own local scope. For example, we can add a while loop to our main() that has its own local variable also named j:

int main (int argc, const char * argv[])
{
    @autoreleasepool {

        int j = 10;
        int k = 20;
        int result;

        while (k > 0)
        {
            int j = 0;
            j =+ k;
            k--;
        }
    }
    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[])
{
   @autoreleasepool {

        NSLog (@"myVar = %i", myVar);
   }
   return 0;
}

As we can see, the global variable myVar is declared outside of the main() function and 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;
void displayit();

int main (int argc, const char * argv[])
{
    @autoreleasepool {

        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:

#import <Foundation/Foundation.h>

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:

clang -framework Foundation main.m displayit.m -o main
displayit.m:7:59: error: use of undeclared identifier 'myVar'
        NSLog (@"MyVar from different source file is %i", myVar);

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 specifier 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 accessed both by code in the same file, or by code in different files. Suppose, however, that you wanted a variable to be 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;
void displayit();

int main (int argc, const char * argv[])
{
    @autoreleasepool {

        NSLog (@"myVar = %i", myVar);

        displayit();
    }
    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 supported by Objective-C 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.

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.

Buy Print



PreviousTable of ContentsNext
Objective-C Dynamic Binding and Typing with the id TypeAn Overview of Objective-C Functions