Objective-C Classes

These notes describe the basics of defining your own custom Objective-C classes and memory managment.

Defining a Class

A class definition is split into two files: an interface (.h) and an implementation (.m). 

Example:

The interface file MyClass.h

    #import < ....>
    @interface MyClass : NSObject
    {
        // instance variables
        NSString *myVar;
    }

    // method declarations
    -(NSString *) myVar;     // getter for the instance variable
    -(void) setMyVar : (NSString *) value; // setter for the instance variable
    -(void) doStuff;
    @end

The implementation file MyClass.m

    #import "MyClass.h"
    @implementation MyClass

    -(NSString *) myVar {
            return myVar;
    }

    -(void) setMyVar : (NSString *) value {
            myVar = value;
    }

    -(void) doStuff {
        ...
    }
    @end

Calling your own methods

Consider the method doStuff which uses the class setter method.  The keyword self is used to call your own method.

    -(void) doStuff {
        [self setMyVar: @"stuff"];
    }

What if this method overrides the method of the super class?  In that case, you may want to let the super class do what needs to be done first.

    -(void) doStuff {
        [super doStuff];    // super class method call
        [self setMyVar: @"stuff"];  // custom behavior
    }

Synthesized Accessor Methods

The getter and setter methods can be synthesized, or basically written for you, as a convenience using the @property directive.

The interface file MyClass.h

    #import < ....>
    @interface MyClass : NSObject
    {
        // instance variables
        NSString *myVar;
    }

    @property NSString *myVar;

    -(void) doStuff;
    @end

The implementation file MyClass.m

    #import "MyClass.h"
    @implementation MyClass

    @synthesize myVar

    -(void) doStuff {
        ...
    }
    @end

You can access the properties using a convenient dot syntax. Instead of [myObject myVar] we can now use myObject.myVar to get the value.  Instead of [myObject setMyVar: value] we can use myObject.myVar = value;

Attributes can be included to property declarations as explained in the documentation.

Allocating and Returning Objects

Object Creation is a two step process.  First you allocate memory use the alloc method and then you initialize the object state with the init method. 

    MyClass *myObject = [[MyClass alloc] init];

You can write your own init method for the class that would do the initialization from the super class and then any specific initialization for your own class.

    -(id) init {
        if (self = [super init]) {
            myVar = @"initial value";
        }
        return self;
    }

Important : For every alloc there must be a dealloc, otherwise you will be leaking memory. 

We need to discuss some memory management now.  Memory management is done with a mechanism called reference counting.  Every object that is created has a retain count.  The alloc and copy methods create objects with a retain count of 1.  There are also methods retain and release which increment and decrement the retain count for an object.  When the count reaches 0, the object is destroyed by having the dealloc method invoked automatically.  You will never invoke the dealloc method yourself but instead use the release method. 

So we will need to write the dealloc method for our class.

    -(void) dealloc {
        //clean up code will need to release instance variables that are owned by the class
        [myVar release];
        // invoke the dealloc of the super class
        [super dealloc];
    }

Now we will examine returning newly created objects from our methods.  Consider the following code:

    -(NSString*) createString {
        NSString *result;
        result = [[NSString alloc] initwithFormat: @"%@ %@", myVar1, myVar2];
        return result;
    }

This will leak memory!  Why? 

The memory for result needs to be release but where?  We could insist the the user releases the value that it received but this is bad form. It can't be released before the return but it can't be released after either.  We will use a method called autorelease.

    -(NSString*) createString {
        NSString *result;
        result = [[NSString alloc] initwithFormat: @"%@ %@", myVar1, myVar2];
        [result autorelease];
        return result;
    }

There is a pool of memory called the autorelease pool.  An object is added to the pool when it is sent the autorelease message.  When the pool is released, so are all the objects that were added to it.  You may have observed the creation and destruction of the pool in your main program from the last lab.