Objective-C

Modified

DEFINITIONS

Objective-C

Cocoa

Xcode

RESOURCES

Programming in Objective C - Lessons and examples by author Stephen Kochan

Basic Objective-C reference

Tracking iPhone memory leaks

Memory Management - Apple's official guide

The Instruments Application - Application tuning and tracking memory leaks

Clang Static Analyzer - Requires Snow Leopard and Xcode 3.2

Zombie - enabling zombie detection

 

1.    GETTING STARTED - Xcode on the Mac

Example 1 - prog1

Xcode can create projects for different target applications.

A command line application is the simplest, avoiding a user interface.

  1. Open Xcode.
  2. File | New Project
  3. Mac OS X | Command Line Utility | Type Foundation
  4. Navigate to a directory for creating the project.
  5. prog1 ( project name )
    • A window listing project files should open as at right.
    • prog1.m is automatically created.
  6. Click Build and Go icon to compile and execute.
  7. To view output in debugger console window:

    Run | Console

 

Example 2 - prog2

Objective C requires a top-level class from which all classes derive.

For iOS, NSObject is the preferred top-level class, predefined in the Foundation.h file.

Below is a simple example that implements and uses a Counter class in the typical Objective C fashion.

  1. Create a new project named prog2
  2. Copy and paste prog2.m below in place of the one Xcode generated.
  3. Create a new file Counter.h by:

    File | New File | Other | Empty File

    Counter.h
     

  4. Repeat for Counter.m
  5. Copy and paste the respective contents below.
  6. Click the Build and Go icon.

 

2.1 OBJECTS/METHODS/MESSAGES

main

Function main() is executed first.

[[Counter alloc] init] is the same message.

prog2.m
#import "Counter.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  Counter *aCounter = [Counter new];

  [aCounter inc];
  [aCounter inc];

  [aCounter print];

  [aCounter release];

  [pool drain];
  return 0;
}

 

2.2 CLASSES/GETTERS and SETTERS

A class consists of two main parts:

interface defines what elements (methods, instance variable, etc.) are visible outside the class.

implementation defines what the elements do and how.

Classes inherit from some top-level class, here NSObject.

Counter has one instance variable, count.

To provide access to instance variable outside the class definition and promote loose coupling, instance variables often have two methods, a setter and getter:

  1. setCount    setter of the value of instance variable count
     
  2. count         getter of the value of instance variable count
Counter.h
#import <Foundation/Foundation.h>

@interface Counter : NSObject {
       int count;
}

-(void) print;

-(void) setCount: (int) n;

-(int) count;

-(void) inc;

@end
Counter.m
#import "Counter.h"

@implementation Counter;

-(void) print { NSLog(@"count = %i ", count );  }

-(void) setCount: (int) n { count = n; }

-(int) count { return count; }

-(void) inc { count++; }

@end
prog2.m
#import "Counter.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  Counter *aCounter = [Counter new];

  [aCounter inc];
  [aCounter inc];

  [aCounter print];

  [aCounter release];

  [pool drain];
  return 0;
}

2.2.5     MESSAGES

Objects are sent messages (method calls).

[aCounter inc];

sends aCounter object the inc message.

 

2.3     INITIALIZATION

Though the count instance variable was initialized to zero by default, better to implement an init method to ensure proper initialization.

init is called by new:

Counter *aCounter = [Counter new];

or directly by:

Counter *aCounter = [[Counter alloc] init];

[super init] calls the super-class initialization; must do first to initialize the object by the super (parent) class init method.

Counter.h
#import <Foundation/Foundation.h>

@interface Counter : NSObject {
       int count;
}

-(void) print;

-(void) setCount: (int) n;

-(int)    count;

-(void) inc;

-(id) init;

@end

 

Counter.m
#import "Counter.h"

@implementation Counter;

-(void) print {  NSLog(@"count = %i ", count );  }

-(void) setCount: (int) n { count = n; }

-(int) count { return count; }

-(void) inc { count++; }
 
-(id) init {
         [super init];
         count = 0;
         return self;
}

@end

 

2.5    PARAMETER PASSING

Pass-by-value is used in the majority of cases by object oriented languages.

Using Objective C, you should expect everything, primitive types and objects, to be pass-by-value. Otherwise, you're not probably using Objective C.

Objective C can pass-by-value or simulate pass-by-reference. Remember, C is always there.

C is pass-by-value and simulate pass-by-reference. Simulated pass-by-reference in C requires that:

the address of the actual parameter be passed by the caller function (i.e. & operator),

the formal parameter be dereferenced in the called function (i.e. * operator).

Pass-by-reference allows the actual parameter's value to be changed.

Here, parameter a value is change from 4 to 5.

Pass-by-value prevents changing (i.e. side-effecting) the value of the actual parameter.

Below, simulated pass-by-reference is used to implement a swap operation on int actual parameters, the values of the actuals are changed.

Simulated Pass-by-Reference
Pass-by-Reference

 

#import <Foundation/Foundation.h>

@interface Swap : NSObject {}
-(void) swap: (int *) x with: (int *) y;
@end

@implementation Swap

-(void) swap: (int *) x with: (int *) y  {
     int temp = *x;        (500) = (100) = 4
     *x = *y;        (100) = (200) = 5
     *y = temp;        (200) = (500) = 4
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = 
         [[NSAutoreleasePool alloc] init];

    Swap *s = [Swap new];
    int a=4, b=5;
	
    [ s swap: &a with: &b];
	
    NSLog(@"%d %d", a, b);
    [pool drain];
    return 0;
}

Simulated pass-by-reference output is: 5 4

 

Below, pass-by-value is used to implement a swap operation on int actual parameters, the values of the actuals are unchanged.

Pass-by-Value
Pass-by-Value

#import <Foundation/Foundation.h>

@interface Swap : NSObject {}
-(void) swap: (int) x with: (int) y;
@end

@implementation Swap

-(void) swap: (int) x with: (int) y  {
     int temp = x;        (500) = (300) = 4
     x = y;        (300) = (400) = 5
     y = temp;        (400) = (500) = 4
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = 
	[[NSAutoreleasePool alloc] init];
	
    Swap *s = [Swap new];
    int a=4, b=5;
	
    [ s swap: a with: b];
	
    NSLog(@"%d %d", a, b);
    [pool drain];
    return 0;
}

Pass-by-value output is: 4 5

 

2.6    OBJECTS AS PARAMETERS

Object parameters are almost always pass-by-value. Pass-by-reference is used to return results through the actual parameters.

One reason for pass-by-value is memory management becomes more challenging under pass-by-reference.

The receiver of a message has the formal parameter name self, is pass-by-value.

Pass-by-value of the actual parameter passes the parameter's value, though the value is a reference, it is still pass-by-value.

Pass-by-value prevents changing (i.e side-effecting) the value of the actual parameter.

However, when the value is the address of an object, the value of the object can be changed.

The actual parameter still has the same value, the address of the object.

Below, pass-by-value is used to implement -swap: operation on INT actual parameters.

Pass-by-value passes the value of the actual parameter, a reference to an object.

The actual parameter's value, the object reference, is unchanged.

The object attributes are changed by -swap:

Keep in mind that -swap: has two parameters, both pass-by-value:

  1. the message receiver with the formal parameter name self,
  2. the formal parameter with name x.

-(void) swap: (INT *) x;

[a swap: b];

Pass-by-Value

#import <Foundation/Foundation.h>

@interface INT : NSObject {
       int n;
}

-(id) init: (int) N;

-(void) setN : (int) x;

-(int) getN;

-(void) swap: (INT *) x;

@end
 

Pass-by-value

 

@implementation INT

-(id) init: (int) N { 
	[super init];
	n = N; 
	return self; 
}

-(void) setN : (int) N {  n = N;  }

-(int) getN { return n;   }

-(void) swap: (INT *) x  {
     int temp = [ self getN ];        (500) = (111) = 4
 
     [self setN: [ x getN ]];        (111) = (222) = 5
 
     [x setN: temp];        (222) = (500) = 4
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = 
                    [[NSAutoreleasePool alloc] init];

    INT *a = [[INT alloc] init: 4];
    INT *b = [[INT alloc] init: 5];
	
    [a swap: b];
	
    NSLog(@"%d %d", [ a getN ], [ b getN ]);
    [pool drain];
    return 0;
}

Output is: 5 4

Note: -getN is the accessor method by convention named -n.

 

4. INHERITANCE

The Counter class will be extended by dec (a decrement method).

self is the receiver when sending a message to the current object (this in Java and C++).

prog2.m
#import "DecCounter.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  DecCounter *aCounter = [DecCounter new];

  [aCounter inc];
  [aCounter print];

  [aCounter dec];
  [aCounter dec];

  [aCounter print];

  [pool drain];
  return 0;
}

2009-11-27 12:50:03.421 Counter[3877:10b] count = 1
2009-11-27 12:50:03.422 Counter[3877:10b] count = -1

DecCounter.h
#import "Counter.h"

@interface DecCounter : Counter { }

-(void) dec;

@end
DecCounter.m
#import "DecCounter.h"

@implementation DecCounter;

-(void) dec { [self setCount: [self count] - 1]; }

@end

All Counter methods and instance variables are inherited.

By default, instance variables are protected, directly accessible by inherited classes.

Counter.h
#import <Foundation/Foundation.h>

@interface Counter : NSObject {
       int count;
}

-(void) print;

-(void) setCount: (int) n;

-(int) count;

-(void) inc;

@end
Counter.m
#import "Counter.h"

@implementation Counter;

-(void) print { NSLog(@"count = %i ", count );  }

-(void) setCount: (int) n { count = n; }

-(int) count { return count; }

-(void) inc { count++; }

@end

 

4.5 POLYMORPHISM

Example

To demonstrate polymorphism, a who method has been added to both the Counter and DecCounter.

who method is invoked in print.

Polymorphism determines which who method is invoked, dependent upon whether the object is a Counter or DecCounter.

Counter.h
#import <Foundation/Foundation.h>

@interface Counter : NSObject {
       int count;
}

-(void) print;

-(void) setCount: (int) n;

-(int)    count;

-(void) inc;

-(NSString *) who;
@end

Counter.m
#import "Counter.h"

@implementation Counter;

-(void) print { 
       NSLog(@"count = %i %@", count, [self who] ); 
}

-(void) setCount: (int) n { count = n; }

-(int) count { return count; }

-(void) inc { count++; }

-(NSString *) who { return @"Counter"; }
@end

DecCounter.h
#import "Counter.h"

@interface DecCounter : Counter { }

-(void) dec;

-(NSString *) who;

@end

DecCounter.m
#import "DecCounter.h"

@implementation DecCounter;

-(void) dec { [self setCount: [self count] - 1]; }

-(NSString *) who { return @"DecCounter"; }

@end

prog2.m
#import "DecCounter.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  Counter *counter = [Counter new];
  DecCounter *decCounter = [DecCounter new];

  [counter inc];
  [counter print];

  [decCounter dec];
  [decCounter print];

  [pool drain];
  return 0;
}

 

4.6 OVER-RIDING AND PRIVATE METHODS

Suppose we need a modulus 10 counter (Mod10Counter) based on the Counter class.

Over-riding the inc method of Counter allows specializing a inc method that will be accessed by Mod10Counter objects.

There is also a private method that should not be visible (callable) outside Mod10Counter class implementation.

In the following, inc is public and mod10 is private.

The interface of a private method is defined within the same file as the implementation.

Mod10Counter.h
#import "Counter.h"

@interface Mod10Counter : Counter { }

  -(void) inc;

@end
Mod10Counter.m
#import "Mod10Counter.h"

@interface Mod10Counter ()

-(void) mod10;

@end

@implementation Mod10Counter;

-(void) inc { [self mod10]; }

-(void) mod10 {
   [self setCount: ([self count] + 1) % 10];
}

@end
Counter.h
#import <Foundation/Foundation.h>

@interface Counter : NSObject {
       int count;
}

-(void) print;

-(void) setCount: (int) n;

-(int) count;

-(void) inc;

@end
Counter.m
#import "Counter.h"

@implementation Counter;

-(void) print { NSLog(@"count = %i ", count );  }

-(void) setCount: (int) n { count = n; }

-(int) count { return count; }

-(void) inc { count++; }

@end

 

4.7 PUBLIC/PRIVATE/PROTECTED INSTANCE VARIABLES

Can specify access to instance variables by:

Counter.h
#import <Foundation/Foundation.h>

@interface Counter : NSObject {

@private
       int count;
}

-(void) print;

-(void) setCount: (int) n;

-(int)    count;

-(void) inc;
@end

 

4.8 CATEGORIES - Chapter 22 of COCOA PROGRAMMING

DecCounter inherited Counter class methods and instance variables, adding a dec method.

Inheritance

DecCounter.h
#import "Counter.h"

@interface DecCounter : Counter { }

-(void) dec;

@end
DecCounter.m
#import "DecCounter.h"

@implementation DecCounter;

-(void) dec { [self setCount: [self count] - 1]; }

@end

Use

prog2.m
#import "DecCounter.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  Counter *counter = [Counter new];

  [counter inc];
  [counter print];

  [counter dec];
  [counter print];

  [pool drain];
  return 0;
}

Note that a parent class object should not be the receiver of a child class message or assigned to a child.

[counter dec];

compiles with a warning that counter does not respond to dec and crashes when run.

DecCounter *decCounter = [Counter new];
[ decCounter dec ];

compiles but crashes, a Counter object does not have a dec message.

Why not add the dec method to Counter?

Counter source code may not be available.

 

Category extends a class definition by adding methods only, not instance variables, to an existing class.

The key benefit is that a parent class object can be the receiver of a category message (i.e. method).

[counter dec]; compiles cleanly.

And we don't need the Counter source.

 

Category

DecCounter.h
#import "Counter.h"

@interface Counter (DecCounter)

-(void) dec;

@end
DecCounter.m
#import "DecCounter.h"

@implementation Counter (DecCounter);

-(void) dec { [self setCount: [self count] - 1]; }

@end

Use

prog2.m
#import "DecCounter.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  Counter *counter = [Counter new];

  [counter inc];
  [counter print];

  [counter dec];
  [counter print];

  [pool drain];
  return 0;
}

 

4.9    OBJECT DESCRIPTION

Objects inherit from a top-level class such as NSObject.

description method, by default, returns the class name and object address in memory.

Over-riding the description method allows programmer defined descriptions, useful for debugging.

For example, the following displays the Counter object description to the debugging console:

Counter *counter = [[Counter alloc] init];

NSLog(@" %@ ", counter );

using the following description definition prints:

Counter: 0

A description method should return an NSString object of the values of instance variables and the class name, as in the following Counter with instance variable count:

-(NSString *) description {
    NSString *result;
    result = [[NSString alloc] initWithFormat:@"Counter: %i ", count];
    [result autorelease];
    return result;
}

The above can serve as a model description method, however there are memory management issues which are discussed next.

 

5. MEMORY MANAGEMENT

Variables automatically allocated on stack on method entry:

parameters

local variables

deallocated automatically by adjusting the stack on method exit.

 

Objects allocated on heap by:

new

alloc

malloc

deallocated manually at appropriate time.

 

Mac OS X supports garbage collection but the current iOS does not, for performance. Running the garbage collection could cause the UI to stall.

 

Common Human Errors

 

Heap

Program variables reference allocated heap memory (result of new, alloc etc.)

Program variables are allocated on the stack but may reference heap memory.

The following is a linked list.

 

Garbage Collection using Reference Counting

Garbage collection automatically recovers freed memory, avoiding most of the human errors.

Memory is allocated in cells, with reference to other memory cells added as program executes.

Each cell has a reference count reflecting the number of referencing cells.

When variable is no longer visible, run-time system automatically calls garbage collector.

void decrement( Cell c) {
     c.count--;

     if(c.count == 0) {
       decrement(c.right);
       decrement(c.left);
       delete c;
     }
}

When a variable reference is released, the reference count is decremented.

Using the algorithm above, the garbage collector follows references to decrement each reference count.

 

 

Problems

 

Human Reference Counting

Reference counting is used to determine when an object storage can be reclaimed.

Each object has a retain count field for reference counting.

alloc allocates an object and sets the retain count = 1.

retain increments the retain count.

release decrements the retain count.

autorelease decrements the retain count, often at end of application or UI event loop.

dealloc called when retain count = 0.

retainCount returns the retain count of an object. Widely viewed by developers to be unreliable.

 

Rules for the programmer

  1. Objects created by alloc, new, copy or mutableCopy have a retain count of 1 and should be released.

    NSString *s = [[ NSString alloc] stringWithString: @"Hello World"];

    [s release];

  2. Objects obtained by any other method will be automatically released, unless retained.

    NSString *s = @"Hello World";

    or

    NSString *s = [ @"Hello World" retain];

    [s release];

 

Example

Reconsider the Counter example, which releases allocated memory.

Counter.h
#import <Foundation/Foundation.h>

@interface Counter : NSObject {
       int count;
}

-(void) print;

-(void) setCount: (int) n;

-(int) count;

-(void) inc;

@end
Counter.m
#import "Counter.h"

@implementation Counter;

-(void) print { NSLog(@"count = %i ", count );  }

-(void) setCount: (int) n { count = n; }

-(int) count { return count; }

-(void) inc { count++; }

-(void) dealloc {
           NSLog(@"dealloc Counter");
           [super dealloc];
}

@end
prog2.m
#import "Counter.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  Counter *aCounter = [[Counter alloc] init];

  [aCounter inc];
  [aCounter inc];

  [aCounter print];

  [aCounter release];

  [pool drain];
  return 0;
}
Counter *aCounter = [ [Counter alloc] init];

 

Allocates storage for a Counter object which includes instance variables and a reference count, automatically set to 1 when the object is created.

[aCounter release];

 

 

Decrements the reference count, which when 0, the object -dealloc method is called and memory is reclaimed.

 

Example

Counter objects do not have instances of other objects, only memory for an int so releasing a Counter object deallocates all memory.

However, an object that contains an object is responsible for deallocating those objects.

@interface TwoCounters : NSObject {
    Counter * one, * two;
}
-(id) init;
-(void) dealloc;
@end

TwoCounters * a = [TwoCounters new];

 

-(id) init {
  [super init];
  one = [Counter new];
  two = [Counter new];
  return self;
}

Allocates storage for a TwoCounters object which in turn allocates two Counter objects, the reference count of all objects is 1.

 

[a release];

 

-(void) dealloc {
   [super dealloc];
}

Decrements the reference (retain) count of the TwoCounter object.

When 0, -dealloc called, the TwoCounters object memory is reclaimed.

one and two instance variables' memory is reclaimed.

However, the Counter objects referenced by one and two remain.

 [a release] calls the TwoCounters object's -dealloc method, if one exists, when retain count is 0.

[a release];

-(void) dealloc {
   [one release];
   [two release];
   [super dealloc];
}

Now the TwoCounters -dealloc releases the one and two Counter objects.

a variable remains but with a dangling reference to deallocated memory.

Any use of a is incorrect, and will, if we're lucky, cause a noticeable failure if we try to use it.

prog2.5.m
#import "Counter.h"

@interface TwoCounters : NSObject 
{
	Counter * one, * two;
}
-(id) init;
-(void) dealloc;
@end
@implementation TwoCounters

-(id) init {
	[super init];
	one = [Counter new];
	two = [Counter new];
	return self;
}

-(void) dealloc {
	[one release];
	[two release];
	[super dealloc];
}
@end

int main(int argc, char *argv[]) {
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	
	TwoCounters *a = [TwoCounters new];
	
	[a release];
	
	[pool drain];
	return 0;
}

 

6. ARRAYS - AGGREGATE DATA

NSMutableArray *array;

array = [NSMutableArray new];

[array addObject: @"String 1"];

[array addObject: @"String 2"];

[array addObject: [[NSNumber alloc] initWithInt: 42]];

for( (id) element in array)

     NSLog(@" %@ ", element) ;

Outputs:    String 1 String 2 42

 

Example

Counters is an aggregate of multiple DecCounter's in an array.

NSMutableArray class allows array elements to be changed.

description method returns a NSString describing the Counters object.

NSLog(@"%@", counters) calls the method, due to the %@ format.

Counters.h
#import <Foundation/Foundation.h>
#import "DecCounter.h"

@interface Counters : NSObject
{
   NSString *name;
   NSMutableArray *array;
}

-(id) init: (NSString *) theName;

-(NSString *) description;

-(void) print;

@end

prog3.m
#import "Counters.h"

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

  NSAutoreleasePool * pool =
                  [[NSAutoreleasePool alloc] init];

  Counters *counters =
                  [[Counters alloc] init: @"Array"];

  NSLog(@"%@", counters);

  [counters print];

  [pool drain];
  return 0;
}
Counters.m
#import "Counters.h"

@implementation Counters

-(id) init: (NSString *) theName {
   int i;

   [super init];
   name = theName;

   array = [NSMutableArray new];

   for(i=0; i < 5; i++) {
      DecCounter *dc = [DecCounter new];
      [array addObject: dc];
   }
   return self;
}

-(void) print {
    for( DecCounter *dc in array)
        [dc print];
}

-(NSString *) description {
    NSString *result;
    result = [[NSString alloc] initWithFormat:@"%@ ",name];
    return result;
}

@end

 

Assuming counters references an object as in the following diagram:

NSLog(@"%@", counters);

[counters print];

produces the debugging console output of:

2009-11-27 19:53:09.334 Counter[4531:10b] Array
2009-11-27 19:53:09.335 Counter[4531:10b] count = 4
2009-11-27 19:53:09.336 Counter[4531:10b] count = 7
2009-11-27 19:53:09.336 Counter[4531:10b] count = 8
2009-11-27 19:53:09.337 Counter[4531:10b] count = 6
2009-11-27 19:53:09.338 Counter[4531:10b] count = 4

 

6.1    MORE MEMORY MANAGEMENT

release

prog3.m has a memory leak.

Counters object memory was allocated but never reclaimed, until the program terminated.

Counters *counters = [[Counters alloc] init: @"Array"];

[counters release]; decrements the retain count to 0; Counters object memory is reclaimed.

prog4.m
#import "Counters.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  Counters *counters = [[Counters alloc] init: @"Array"];

  NSLog(@"%@", counters);

  [counters print];

  [counters release];

  [pool drain];
  return 0;
}

Counters.h
@interface Counters : NSObject
{
   NSString *name;
   NSMutableArray *array;
}
@end

 

dealloc

Still a memory leak.

[counters release];

reclaimed the Counters object memory for instance variables name and array but not the memory referenced by the instance variables name and array.

The object user allocates using alloc or new and should release the object.

retain claims the object for a user and should be released.

However, releasing memory referenced by an object should not be the responsibility of the user of the object.

dealloc over-rides the parent method to release memory used by an object.

Called when an object with a retain count of 1 is sent a release.

dealloc is called when the main() method sends [counters release].

-(void) dealloc {
   [name release];
   [array release];
   [super dealloc];
}

Still a memory leak.

[array release]; reclaimed the NSMutableArray.

When objects are added to a NSMutableArray, the retain count of each object is incremented.

When NSMutableArray is released, each object reference is decremented.

Problem is each DecCounter object reference was incremented twice:

  1. DecCounter *dc = [DecCounter new];
  2. [array addObject: dc];
-(id) init: (NSString *) theName {
   int i;

   [super init];
   name = theName;

   array = [NSMutableArray new];

   for(i=0; i < 5; i++) {
      DecCounter *dc = [DecCounter new];
      [dc setCount: (random() % 10 + 1)];
      [array addObject: dc];
   }
   return self;
}

Solution can be implemented in either the init or dealloc but not both.

Here, each array entry is manually released, then the array is released which releases each entry again.

-(void) dealloc {
   [name release];

   for( DecCounter *dc in array)
      [dc release];

   [array release];
   [super dealloc];
}

finally is deallocated to:

 

autorelease

Still a memory leak.

-(NSString *) description {
    NSString *result;
    result = [[NSString alloc] initWithFormat:@"%@ ",name];
    return result;
}

description method allocates and returns result, an NSString object.

The calling method would normally retain a returned object, incrementing the retain count, then release the object when no longer needed.

However, the reference count would still be 1 due to the alloc above.

Releasing the object immediately decrements the reference count, so:

[result release];
return result;

returns a dangling reference to the caller.

The following delays the object release allowing the calling method to execute a retain.

[result autorelease];
return result;

We assume that the calling method will retain the returned object if it wants and eventually release the object, decrementing the retain count; or allow the object to be released automatically at the end of the event loop.

Counters.m
#import "Counters.h"

@implementation Counters

-(id) init: (NSString *) theName {
   int i;

   [super init];
   name = theName;

   array = [NSMutableArray new];

   for(i=0; i < 5; i++) {
      DecCounter *dc = [DecCounter new];
      [dc setCount: (random() % 10 + 1)];
      [array addObject: dc];
   }
   return self;
}

-(void) print {
   for( DecCounter *dc in array)  [dc print];
}

-(NSString *) description {
   NSString *result;
   result = [[NSString alloc] initWithFormat:@"%@ ",name];

   [result autorelease];

   return result;
}

-(void) dealloc {
   [name release];

   for( DecCounter *dc in array)  [dc release];

   [array release];
   [super dealloc];
}

@end

autorelease adds an object to the autorelease pool, consisting of a list of objects that will be sent a release message when the pool is released or drained.

Because the autorelease does not decrement the retain count until the pool is released, the object can continue to be accessed.

It is not a good idea to use autorelease in place of release since memory is occupied until the pool is released, which may not be until the end of the application.

Allocating an object (retain count=1), then releasing (retain count=0) and autoreleasing the object will cause a memory fault when the pool is released.

When the autorelease pool is released, each object is sent a release message. Those with retain count = 0 are deallocated, the rest will survive.

Cocoa's (and iOS's) event loop releases the autorelease pool at the end of the loop.

When using Foundation classes, the pool is managed programmatically as below, while Cocoa automatically performs pool management.

prog4.m
#import "Counters.h"

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

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  Counters *counters = [[Counters alloc] init: @"Array"];

  [counters autorelease];

  NSLog(@"%@", counters);

  [counters print];

  [pool drain];

  return 0;
}

 

retain

No memory leak now but a potential dangling reference.

Keep in mind that alloc or new sets the retain count to 1.

Assume that any object passed as a parameter, the caller has retained.

init receives a NSString reference parameter but does not retain.

name = theName;

Problem that can result:

Should the caller release the theName object, the object could be reclaimed, creating a dangling reference.

Solution is to:

retain the reference parameter

The following init incorporates two solutions:

  1. Prevent a dangling reference due to the parameter object, theName, not being retained
  2. Prevent a memory leak due to allocating DecCounter (retain count=1) and adding to the array (increment retain count).
-(id) init: (NSString *) theName {
   int i;

   [super init];

    [theName retain];
    name = theName;


   array = [NSMutableArray new];

   for(i=0; i < 5; i++) {
      DecCounter *dc = [DecCounter new];
      [dc setCount: (random() % 10 + 1)];
      [array addObject: dc];

       [dc release];

   }
   return self;
}

Counters.m
#import "Counters.h"

@implementation Counters

-(id) init: (NSString *) theName {
   int i;

   [super init];

   [theName retain];
   name = theName;


   array = [NSMutableArray new];

   for(i=0; i < 5; i++) {
      DecCounter *dc = [DecCounter new];
      [dc setCount: (random() % 10 + 1)];
      [array addObject: dc];

      [dc release];

   }
   return self;
}

-(void) print {
   for( DecCounter *dc in array)
      [dc print];
}

-(NSString *) description {
   NSString *result;
   result = [[NSString alloc] initWithFormat:@"%@ ",name];

   [result autorelease];

   return result;
}

-(void) dealloc {
   [name release];
   [array release];
   [super dealloc];
}

@end

 

Xcode Leak analysis tools

Static leak analysis can identify some errors, usually forgetting to release an object.

In Xcode, Build | Build and Analyze

Below a potential leak was detected, two allocations but only one release.

Below displays obvious leaks but misses those due to aliases.

 

 

GETTER/SETTER Issues

Non-objects such as int instance variables do not need to be managed.

Setters of objects must manage those objects.

prog5.m
@interface Example {
    NSString *name;
}
-(id) init: (NSString *) theName;
-(void) setName: (NSString *) theName;
-(void) print;
@end

@implementation Example

-(id) init: (NSString *) theName {
       name=theName;
}

-(void) setName: (NSString *) theName {
     name = theName;
}

-(void) print {
   NSLog(@"%@", name);
}

-(void) dealloc {
   [name release];
   [super dealloc];
}

@end

prog5.m continued
Example *e1 =
    [[Example alloc] init:
         [[NSString alloc] initWithFormat:@"%s", "One"]];

Example *e2 =
    [[Example alloc] init:
         [[NSString alloc] initWithFormat:@"%s", "Two"]];

NSString *title =
         [[NSString alloc] initWithFormat:@"%s", "An Example"];

[e1 setName: title];

[e2 setName: title];

[title release];

[e1 print];

After [e2 setName: title];

Setter problem that results:

[title release];

Caller releasing the theName object, the object could be reclaimed, creating a dangling reference.

Following could fail because name instance variable has dangling reference:

[e1 print];

Adding retain fixes the dangling reference problem, recording 3 references to the same @"An Example" string, two from -init: and one from the pool allocation by @"An Example".

-(id) init: (NSString *) theName {
       [retain theName];
       name=theName;
}

But creates a memory leak if different theName objects are used in multiple -setName: calls to the same .

prog5.m
Example *e1 = [[NSString alloc] initWithFormat:@"%s", "One"];

[e1 setName: "Another"];

-dealloc releases only the most recent of the two retains.

-(void) dealloc {
   [name release];
   [super dealloc];
}

Solution is to:

retain the reference parameter

release the previous reference of the instance variable

theName retain count is first incremented, then name retain count is decremented.

prog5.m
Example *e1 = [[NSString alloc] initWithFormat:@"%s", "One"];

[e1 setName: [[NSString alloc] initWithFormat:@"%s", "Another"]];

 

-(void) setName: (NSString *) theName
{
   [theName retain];
   [name release];
   name = theName;

}

-(void) dealloc {
   [name release];
   [super dealloc];
}

If both referenced the same object, name retain count would be unchanged.

If each reference different objects, name retain count correctly reflects the number of references to each object.

Note that @"One" and @"Another" are autoreleased.

Getters of objects have simple behavior, no need for memory management:

-(NSString *) name
{
  return name;
}

 

@properties/@synthesize

Automatically generated setters can behave as described above, retaining the setter object.

@interface Example {
    NSString *name;
}
@properties (retain, readwrite) NSString *name;
-(id) init: (NSString *) theName;
-(void) print;
@end

@implementation Example
@synthesize name;

-(void) dealloc {
   [name release];
   [super dealloc];
}

The -dealloc releases the last object retained by the setter.

retainCount

Releasing objects having a retain count 0 aborts execution.

The following code displays 1 for the retain count of an allocated object.

NSObject *nsO = [[NSObect alloc] init];

NSLog(@"%d", [nsO retainCount]);

 

7.    Exceptions

Error handling is critical for handling out of the ordinary conditions, such as an attempt to open a file that does not exist.

Exceptions are a formal language construct for controlling out of the ordinary flow of execution.

Exceptions consist of four main elements:

  1. defining an exception
  2. trying code that contains an exception
  3. throwing an exception
  4. catching an exception

The following illustrates the four and the results (at right).

#import <Foundation/Foundation.h>
 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSException *e = [NSException
                        exceptionWithName: @"GenericException"
                        reason: @"An example exception"
                        userInfo: nil];
    NSLog(@"1");       
    @try {
        NSLog(@"2");
        @throw e;
        NSLog(@"3");       
    } @catch (NSException *ex ) { ) {
        NSLog( @"%@: ", ex);
        NSLog(@"4");       
    } @finally {
        NSLog(@"5");
    }   
    NSLog(@"6");   
    [pool release];
    return 0;
}

1

2

An example exception:

4

5

6

 

Exceptions can be thrown by methods to the caller.

Advantages

Disadvantages

Generally, release objects in @finally.

#import <Foundation/Foundation.h>
 
@interface Example : NSObject {}
-(void) foo;
@end
 
@implementation Example
-(void) foo {
    NSException *e = [NSException
                        exceptionWithName: @"GenericException"
                        reason: @"An example exception"
                        userInfo: nil];
    @throw e;
}
@end
 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Example *example = [[Example alloc] init];
   
    NSLog(@"1");       
    @try {
        NSLog(@"2");
        [example foo];
        NSLog(@"3");       
    } @catch (NSException *ex ) {
        NSLog( @"%@: ", ex);
        NSLog(@"4");       
    } @finally {
        NSLog(@"5");
        [example release];  
    }   
    NSLog(@"6"); 
    [pool release];
    return 0;
}

1

2

An example exception:

4

5

6

 

re-throwing

Exceptions can be re-thrown, passing up the call chain.

Notice that @finally is executed, though statements following @throw are not.

#import <Foundation/Foundation.h>
 
@interface Example : NSObject {}
-(void) foo;
@end
 
@implementation Example
-(void) foo {
    NSException *e = [NSException
                        exceptionWithName: @"GenericException"
                        reason: @"An example exception"
                        userInfo: nil];
    @throw e;
}
@end
 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Example *example = [[Example alloc] init];
   
    NSLog(@"1");       
    @try {
        NSLog(@"2");
        [example foo];
        NSLog(@"3");       
    } @catch (NSException *ex ) {
        NSLog(@"%@: ", ex);
        @throw;   
        NSLog(@"4");    
    } @finally {
        NSLog(@"5");
        [example release];
    }   
    NSLog(@"6");   
    [pool release];
    return 0;
}

1

2

An example exception:

5

*** Terminating app due to uncaught
exception 'GenericException', reason:
'An example exception'

 

 

Uncaught exception

Throwing without catching an exception results in program termination.

#import <Foundation/Foundation.h>
 
@interface Example : NSObject {}
-(void) foo;
@end
 
@implementation Example
-(void) foo {
    NSException *e = [NSException
                        exceptionWithName: @"GenericException"
                        reason: @"An example exception"
                        userInfo: nil];
    @throw e;
}
@end
 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Example *example = [[Example alloc] init];
   
    NSLog(@"1");       
    [example foo];

    NSLog(@"2");   
    [example release];
    [pool release];
    return 0;
}

1

*** Terminating app due to uncaught
exception 'GenericException',
reason: 'An example exception'

 

 

Subclassing exceptions

Subclassing NSException defines specific types of exceptions to be thrown.

Multiple @catch statements, should be arranged from most specific to most general (NSException).

#import <Foundation/Foundation.h>

@interface MyException : NSException {}
@end

@implementation MyException 
@end

@interface Example : NSObject {}
-(void) foo;
@end

@implementation Example
-(void) foo {
	MyException *e = 
                             [MyException
			exceptionWithName: @"MyException"
			reason: @"An example MyException"
			userInfo: nil];	
	@throw e;
}
@end

int main (int argc, const char * argv[]) {
	NSAutoreleasePool *pool = 
                                         [[NSAutoreleasePool alloc] init];
	Example *example = [[Example alloc] init];
	
	NSLog(@"1");		
	@try {
		NSLog(@"2");
        		[example foo];
		NSLog(@"3");		
	} @catch (MyException *ex ) {
		NSLog( @"%@: ", ex);
		NSLog(@"4 MyException");		
	} @catch (NSException *ex ) {
		NSLog( @"%@: ", ex);
		NSLog(@"4 NSException");		
	} @finally {
		NSLog(@"5");
	          [example release];
	}    
	NSLog(@"6");	
	[pool release];
    return 0;
}
1
2
An example MyException:
4 MyException
5
6