Outline: Downloading This Tutorial
Outline: Downloading This Tutorial
Outline
• Getting Started
o Downloading this tutorial
o Setting up the environment
o Preamble
o Making hello world
• Creating Classes
o @interface
o @implementation
o Piecing it together
• The Details...
o Multiple Parameters
o Constructors
o Access Privledges
o Class level access
o Exceptions
• Inheritance, Polymorphism, and other OOP features
o The id type
o Inheritance
o Dynamic types
o Categories
o Posing
o Protocols
• Memory Management
o Retain and Release
o Dealloc
o Autorelease Pool
• Foundation Framework Classes
o NSArray
o NSDictionary
• Pros and Cons
• More Information
• Getting Started
o Downloading this tutorial
All the source code for this beginners guide including
makefiles is available by downloading objc.tar.gz. Many of the
examples in this tutorial were written by Steve Kochan in the
book Programming in Objective-C. If you want more detailed
information and examples, feel free to check out his book. The
examples on this site were used with his permission, so please
don't copy them.
o Setting up the environment
Linux/FreeBSD: Install GNUStep
In order to build GNUstep applications one must
first execute the GNUstep.sh file in
/usr/GNUstep/System/Makefiles/GNUstep.sh. This path
depends on your system. Some put it in /usr, some
/usr/lib, some /usr/local. If your shell is a csh/tcsh based
shell, you'll want to execute GNUStep.csh instead. It's
recommended that you put this script in your .bashrc
or .cshrc.
Mac OS X: Install XCode
Windows NT 5.X: Install cygwin or mingw and then
install GNUStep
o Preamble
This tutorial assumes you have some basic C
knowledge, including C data types, what a function is, what a
return value is, knowledge of pointers and basic memory
management in C. If you haven't gotten this far, I highly
suggest you pick up K and R's book, The C Programming
Language. This is the book on C written by the writers of C.
Objective-C, being a C derivative, inherits all of C's
features. There are a few exceptions but they don't really
deviate from what C offers as a language.
nil: In C/C++ you're probably used to NULL. In
Objective-C it is nil. The difference is you can pass messages to
nil (such as [nil message];) and this is perfectly legal. You
cannot however do this with NULL.
BOOL: C doesn't have an official boolean type, and in
reality neither does Objective-C. It's however built into the
Foundation classes (Namely from importing NSObject.h). nil is
also included in this header file. BOOL in Objective-C has two
modes, YES and NO rather than TRUE and FALSE.
#import vs #include: As you will notice in the hello
world example, #import was used. #import is supported by the
gcc compiler, however it is deprecated in favor of #include.
#import is basically the same thing as #ifndef #define #endif at
the top and bottom of every .h file you make. I find this to be
retarded, as many other programmers will most likely agree.
For all purposes, just use #import. It's less hassle, and if gcc
ever does remove it chances are enough Objective-C
developers exist to either keep it from getting removed or
getting added back in. As an aside, Apple officially uses
#import in all their code so if this ever did happen, you can be
certain that Apple would conviently ship a forked version of
gcc to add this back in.
The word method and message are used interchangably
in Objective-C, although messages have special properties. A
message can be dynamically forwarded to another object.
Calling a message on an object in Objective-C doesn't mean
that the object implements that message, just that it knows how
to respond to it somehow via directly implementing it or
forwarding the message to an object that does know how to.
o Making hello world
hello.m
#import <stdio.h>
int main( int argc, const char *argv[] ) {
printf( "hello world\n" );
return 0;
}
output
hello world
• Creating classes
o @interface
Based on an example in "Programming in Objective-C,"
Copyright © 2004 by Sams Publishing. Used with permission
Fraction.h
#import <Foundation/NSObject.h>
@interface Fraction: NSObject {
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end
NSObject: Short for NeXTStep Object. Although this is
less meaningful today since it's really OpenStep.
Inheritance is specified as Class: Parent, as seen with
Fraction: NSObject.
Instance variables go between @interface Class: Parent
{ .... }
No access is set (protected, public, private). Default is
protected. Setting the access will be shown later
Instance methods follow after the member variables.
The format is: scope (returnType) methodName:
(parameter1Type) parameter1Name;
scope refers to class or instance. instance
methods begin with - class level methods begin with +
Interface ends with @end
o @implementation
Based on an example in "Programming in Objective-C,"
Copyright © 2004 by Sams Publishing. Used with permission
Fraction.m
#import "Fraction.h"
#import <stdio.h>
@implementation Fraction
-(void) print {
printf( "%i/%i", numerator, denominator );
}
-(void) setNumerator: (int) n {
numerator = n;
}
-(void) setDenominator: (int) d {
denominator = d;
}
-(int) denominator {
return denominator;
}
-(int) numerator {
return numerator;
}
@end
output
• The details...
o Multiple Parameters
Up until this point I haven't showed any way to specify
multiple parameters. It's not as intuitive at first, but it's syntax is
a welcome addition from Smalltalk
Based on an example in "Programming in Objective-C,"
Copyright © 2004 by Sams Publishing. Used with permission
Fraction.h
...
-(void) setNumerator: (int) n andDenominator:
(int) d;
...
Fraction.m
...
-(void) setNumerator: (int) n andDenominator:
(int) d {
numerator = n;
denominator = d;
}
...
main.m
#import <stdio.h>
#import "Fraction.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac = [[Fraction alloc] init];
Fraction *frac2 = [[Fraction alloc] init];
// set the values
[frac setNumerator: 1];
[frac setDenominator: 3];
// combined set
[frac2 setNumerator: 1 andDenominator: 5];
// print it
printf( "The fraction is: " );
[frac print];
printf( "\n" );
// print it
printf( "Fraction 2 is: " );
[frac2 print];
printf( "\n" );
// free memory
[frac release];
[frac2 release];
return 0;
}
output
The fraction is: 1/3
Fraction 2 is: 1/5
Fraction.m
...
-(Fraction*) initWithNumerator: (int) n
denominator: (int) d {
self = [super init];
if ( self ) {
[self setNumerator: n andDenominator:
d];
}
return self;
}
...
main.m
#import <stdio.h>
#import "Fraction.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac = [[Fraction alloc] init];
Fraction *frac2 = [[Fraction alloc] init];
Fraction *frac3 = [[Fraction alloc]
initWithNumerator: 3 denominator: 10];
// set the values
[frac setNumerator: 1];
[frac setDenominator: 3];
// combined set
[frac2 setNumerator: 1 andDenominator: 5];
// print it
printf( "The fraction is: " );
[frac print];
printf( "\n" );
printf( "Fraction 2 is: " );
[frac2 print];
printf( "\n" );
printf( "Fraction 3 is: " );
[frac3 print];
printf( "\n" );
// free memory
[frac release];
[frac2 release];
[frac3 release];
return 0;
}
output
The fraction is: 1/3
Fraction 2 is: 1/5
Fraction 3 is: 3/10
Access.m
#import "Access.h"
@implementation Access
@end
main.m
#import "Access.h"
#import <stdio.h>
int main( int argc, const char *argv[] ) {
Access *a = [[Access alloc] init];
// works
a->publicVar = 5;
printf( "public var: %i\n", a-
>publicVar );
// doesn't compile
//a->privateVar = 10;
//printf( "private var: %i\n", a-
>privateVar );
[a release];
return 0;
}
output
public var: 5
As you an see, instead of private: [list of vars] public:
[list of vars] like in C++, it's just @private, @protected, etc.
o Class level access
Often it's nice to have class level variables and
functions, for instance when keeping track of the # of times an
object has been instanciated.
ClassA.h
#import <Foundation/NSObject.h>
static int count;
@interface ClassA: NSObject
+(int) initCount;
+(void) initialize;
@end
ClassA.m
#import "ClassA.h"
@implementation ClassA
-(id) init {
self = [super init];
count++;
return self;
}
+(int) initCount {
return count;
}
+(void) initialize {
count = 0;
}
@end
main.m
#import "ClassA.h"
#import <stdio.h>
int main( int argc, const char *argv[] ) {
ClassA *c1 = [[ClassA alloc] init];
ClassA *c2 = [[ClassA alloc] init];
// print count
printf( "ClassA count: %i\n", [ClassA
initCount] );
ClassA *c3 = [[ClassA alloc] init];
// print count again
printf( "ClassA count: %i\n", [ClassA
initCount] );
[c1 release];
[c2 release];
[c3 release];
return 0;
}
output
ClassA count: 2
ClassA count: 3
CupWarningException.m
#import "CupWarningException.h"
@implementation CupWarningException
@end
CupOverflowException.h
#import <Foundation/NSException.h>
@interface CupOverflowException: NSException
@end
CupOverflowException.m
#import "CupOverflowException.h"
@implementation CupOverflowException
@end
Cup.h
#import <Foundation/NSObject.h>
@interface Cup: NSObject {
int level;
}
-(int) level;
-(void) setLevel: (int) l;
-(void) fill;
-(void) empty;
-(void) print;
@end
Cup.m
#import "Cup.h"
#import "CupOverflowException.h"
#import "CupWarningException.h"
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
@implementation Cup
-(id) init {
self = [super init];
if ( self ) {
[self setLevel: 0];
}
return self;
}
-(int) level {
return level;
}
-(void) setLevel: (int) l {
level = l;
if ( level > 100 ) {
// throw overflow
NSException *e = [CupOverflowException
exceptionWithName:
@"CupOverflowException"
reason: @"The level is above 100"
userInfo: nil];
@throw e;
} else if ( level >= 50 ) {
// throw warning
NSException *e = [CupWarningException
exceptionWithName:
@"CupWarningException"
reason: @"The level is above or at
50"
userInfo: nil];
@throw e;
} else if ( level < 0 ) {
// throw exception
NSException *e = [NSException
exceptionWithName:
@"CupUnderflowException"
reason: @"The level is below 0"
userInfo: nil];
@throw e;
}
}
-(void) fill {
[self setLevel: level + 10];
}
-(void) empty {
[self setLevel: level - 10];
}
-(void) print {
printf( "Cup level is: %i\n", level );
}
@end
main.m
#import "Cup.h"
#import "CupOverflowException.h"
#import "CupWarningException.h"
#import <Foundation/NSString.h>
#import <Foundation/NSException.h>
#import <Foundation/NSAutoreleasePool.h>
#import <stdio.h>
int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool =
[[NSAutoreleasePool alloc] init];
Cup *cup = [[Cup alloc] init];
int i;
// this will work
for ( i = 0; i < 4; i++ ) {
[cup fill];
[cup print];
}
// this will throw exceptions
for ( i = 0; i < 7; i++ ) {
@try {
[cup fill];
} @catch ( CupWarningException *e ) {
printf( "%s: ", [[e name] cString]
);
} @catch ( CupOverflowException *e ) {
printf( "%s: ", [[e name] cString]
);
} @finally {
[cup print];
}
}
// throw a generic exception
@try {
[cup setLevel: -1];
} @catch ( NSException *e ) {
printf( "%s: %s\n", [[e name]
cString], [[e reason] cString] );
}
// free memory
[cup release];
[pool release];
}
output
Cup level is: 10
Cup level is: 20
Cup level is: 30
Cup level is: 40
CupWarningException: Cup level is: 50
CupWarningException: Cup level is: 60
CupWarningException: Cup level is: 70
CupWarningException: Cup level is: 80
CupWarningException: Cup level is: 90
CupWarningException: Cup level is: 100
CupOverflowException: Cup level is: 110
CupUnderflowException: The level is below 0
Fraction.m
#import "Fraction.h"
#import <stdio.h>
@implementation Fraction
-(Fraction*) initWithNumerator: (int) n
denominator: (int) d {
self = [super init];
if ( self ) {
[self setNumerator: n andDenominator:
d];
}
return self;
}
-(void) print {
printf( "%i / %i", numerator,
denominator );
}
-(void) setNumerator: (int) n {
numerator = n;
}
-(void) setDenominator: (int) d {
denominator = d;
}
-(void) setNumerator: (int) n andDenominator:
(int) d {
numerator = n;
denominator = d;
}
-(int) denominator {
return denominator;
}
-(int) numerator {
return numerator;
}
@end
Complex.h
#import <Foundation/NSObject.h>
@interface Complex: NSObject {
double real;
double imaginary;
}
-(Complex*) initWithReal: (double) r
andImaginary: (double) i;
-(void) setReal: (double) r;
-(void) setImaginary: (double) i;
-(void) setReal: (double) r andImaginary:
(double) i;
-(double) real;
-(double) imaginary;
-(void) print;
@end
Complex.m
#import "Complex.h"
#import <stdio.h>
@implementation Complex
-(Complex*) initWithReal: (double) r
andImaginary: (double) i {
self = [super init];
if ( self ) {
[self setReal: r andImaginary: i];
}
return self;
}
-(void) setReal: (double) r {
real = r;
}
-(void) setImaginary: (double) i {
imaginary = i;
}
-(void) setReal: (double) r andImaginary:
(double) i {
real = r;
imaginary = i;
}
-(double) real {
return real;
}
-(double) imaginary {
return imaginary;
}
-(void) print {
printf( "%_f + %_fi", real, imaginary );
}
@end
main.m
#import <stdio.h>
#import "Fraction.h"
#import "Complex.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac = [[Fraction alloc]
initWithNumerator: 1 denominator: 10];
Complex *comp = [[Complex alloc]
initWithReal: 10 andImaginary: 15];
id number;
// print fraction
number = frac;
printf( "The fraction is: " );
[number print];
printf( "\n" );
// print complex
number = comp;
printf( "The complex number is: " );
[number print];
printf( "\n" );
// free memory
[frac release];
[comp release];
return 0;
}
output
The fraction is: 1 / 10
The complex number is: 10.000000 + 15.000000i
Rectangle.m
#import "Rectangle.h"
#import <stdio.h>
@implementation Rectangle
-(Rectangle*) initWithWidth: (int) w height:
(int) h {
self = [super init];
if ( self ) {
[self setWidth: w height: h];
}
return self;
}
-(void) setWidth: (int) w {
width = w;
}
-(void) setHeight: (int) h {
height = h;
}
-(void) setWidth: (int) w height: (int) h {
width = w;
height = h;
}
-(int) width {
return width;
}
-(int) height {
return height;
}
-(void) print {
printf( "width = %i, height = %i", width,
height );
}
@end
Square.h
#import "Rectangle.h"
@interface Square: Rectangle
-(Square*) initWithSize: (int) s;
-(void) setSize: (int) s;
-(int) size;
@end
Square.m
#import "Square.h"
@implementation Square
-(Square*) initWithSize: (int) s {
self = [super init];
if ( self ) {
[self setSize: s];
}
return self;
}
-(void) setSize: (int) s {
width = s;
height = s;
}
-(int) size {
return width;
}
-(void) setWidth: (int) w {
[self setSize: w];
}
-(void) setHeight: (int) h {
[self setSize: h];
}
@end
main.m
#import "Square.h"
#import "Rectangle.h"
#import <stdio.h>
int main( int argc, const char *argv[] ) {
Rectangle *rec = [[Rectangle alloc]
initWithWidth: 10 height: 20];
Square *sq = [[Square alloc] initWithSize:
15];
// print em
printf( "Rectangle: " );
[rec print];
printf( "\n" );
printf( "Square: " );
[sq print];
printf( "\n" );
// update square
[sq setWidth: 20];
printf( "Square after change: " );
[sq print];
printf( "\n" );
// free memory
[rec release];
[sq release];
return 0;
}
output
Rectangle: width = 10, height = 20
Square: width = 15, height = 15
Square after change: width = 20, height = 20
output
square is a member of square class
square is a kind of square class
square is a kind of rectangle class
square is a kind of object class
square responds to setSize: method
square class responds to alloc method
square instance responds to setSize: method
o Categories
When you want to add methods to a class, you typically
extend it. However this solution isn't always perfect, especially
if you want to rewrite the functionality of a class that you don't
have the source code to. Categories allow you to add
functionality to already existing classes without extending
them. Ruby also has similar functionality to this.
Based on an example in "Programming in Objective-C,"
Copyright © 2004 by Sams Publishing. Used with permission
FractionMath.h
#import "Fraction.h"
@interface Fraction (Math)
-(Fraction*) add: (Fraction*) f;
-(Fraction*) mul: (Fraction*) f;
-(Fraction*) div: (Fraction*) f;
-(Fraction*) sub: (Fraction*) f;
@end
FractionMath.m
#import "FractionMath.h"
@implementation Fraction (Math)
-(Fraction*) add: (Fraction*) f {
return [[Fraction alloc]
initWithNumerator: numerator * [f denominator] +
denominator * [f numerator]
denominator:
denominator * [f denominator]];
}
-(Fraction*) mul: (Fraction*) f {
return [[Fraction alloc]
initWithNumerator: numerator * [f numerator]
denominator:
denominator * [f denominator]];
}
-(Fraction*) div: (Fraction*) f {
return [[Fraction alloc]
initWithNumerator: numerator * [f denominator]
denominator:
denominator * [f numerator]];
}
-(Fraction*) sub: (Fraction*) f {
return [[Fraction alloc]
initWithNumerator: numerator * [f denominator] -
denominator * [f numerator]
denominator:
denominator * [f denominator]];
}
@end
main.m
#import <stdio.h>
#import "Fraction.h"
#import "FractionMath.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac1 = [[Fraction alloc]
initWithNumerator: 1 denominator: 3];
Fraction *frac2 = [[Fraction alloc]
initWithNumerator: 2 denominator: 5];
Fraction *frac3 = [frac1 mul: frac2];
// print it
[frac1 print];
printf( " * " );
[frac2 print];
printf( " = " );
[frac3 print];
printf( "\n" );
// free memory
[frac1 release];
[frac2 release];
[frac3 release];
return 0;
}
output
MyClass.m
#import "MyClass.h"
#import <stdio.h>
@implementation MyClass
-(void) publicMethod {
printf( "public method\n" );
}
@end
// private methods
@interface MyClass (Private)
-(void) privateMethod;
@end
@implementation MyClass (Private)
-(void) privateMethod {
printf( "private method\n" );
}
@end
main.m
#import "MyClass.h"
int main( int argc, const char *argv[] ) {
MyClass *obj = [[MyClass alloc] init];
// this compiles
[obj publicMethod];
// this throws errors when compiling
//[obj privateMethod];
// free memory
[obj release];
return 0;
}
output
public method
o Posing
Posing is similar to categories, but with a twist. It
allows you to extend a class, and make your subclass pose (in
place of) the super class globally. For instance: Say you have
NSArrayChild that extends NSArray. If you made
NSArrayChild pose for NSArray all your code would begin
using the NSArrayChild instead of NSArray automatically.
Based on an example in "Programming in Objective-C,"
Copyright © 2004 by Sams Publishing. Used with permission
FractionB.h
#import "Fraction.h"
@interface FractionB: Fraction
-(void) print;
@end
FractionB.m
#import "FractionB.h"
#import <stdio.h>
@implementation FractionB
-(void) print {
printf( "(%i/%i)", numerator,
denominator );
}
@end
main.m
#import <stdio.h>
#import "Fraction.h"
#import "FractionB.h"
int main( int argc, const char *argv[] ) {
Fraction *frac = [[Fraction alloc]
initWithNumerator: 3 denominator: 10];
// print it
printf( "The fraction is: " );
[frac print];
printf( "\n" );
// make FractionB pose as Fraction
[FractionB poseAsClass: [Fraction class]];
Fraction *frac2 = [[Fraction alloc]
initWithNumerator: 3 denominator: 10];
// print it
printf( "The fraction is: " );
[frac2 print];
printf( "\n" );
// free memory
[frac release];
[frac2 release];
return 0;
}
output
The fraction is: 3/10
The fraction is: (3/10)
Fraction.h
#import <Foundation/NSObject.h>
#import "Printing.h"
@interface Fraction: NSObject <Printing,
NSCopying> {
int numerator;
int denominator;
}
-(Fraction*) initWithNumerator: (int) n
denominator: (int) d;
-(void) setNumerator: (int) d;
-(void) setDenominator: (int) d;
-(void) setNumerator: (int) n andDenominator:
(int) d;
-(int) numerator;
-(int) denominator;
@end
Fraction.m
#import "Fraction.h"
#import <stdio.h>
@implementation Fraction
-(Fraction*) initWithNumerator: (int) n
denominator: (int) d {
self = [super init];
if ( self ) {
[self setNumerator: n andDenominator:
d];
}
return self;
}
-(void) print {
printf( "%i/%i", numerator, denominator );
}
-(void) setNumerator: (int) n {
numerator = n;
}
-(void) setDenominator: (int) d {
denominator = d;
}
-(void) setNumerator: (int) n andDenominator:
(int) d {
numerator = n;
denominator = d;
}
-(int) denominator {
return denominator;
}
-(int) numerator {
return numerator;
}
-(Fraction*) copyWithZone: (NSZone*) zone {
return [[Fraction allocWithZone: zone]
initWithNumerator: numerator
den
ominator: denominator];
}
@end
Complex.h
#import <Foundation/NSObject.h>
#import "Printing.h"
@interface Complex: NSObject <Printing> {
double real;
double imaginary;
}
-(Complex*) initWithReal: (double) r
andImaginary: (double) i;
-(void) setReal: (double) r;
-(void) setImaginary: (double) i;
-(void) setReal: (double) r andImaginary:
(double) i;
-(double) real;
-(double) imaginary;
@end
Complex.m
#import "Complex.h"
#import <stdio.h>
@implementation Complex
-(Complex*) initWithReal: (double) r
andImaginary: (double) i {
self = [super init];
if ( self ) {
[self setReal: r andImaginary: i];
}
return self;
}
-(void) setReal: (double) r {
real = r;
}
-(void) setImaginary: (double) i {
imaginary = i;
}
-(void) setReal: (double) r andImaginary:
(double) i {
real = r;
imaginary = i;
}
-(double) real {
return real;
}
-(double) imaginary {
return imaginary;
}
-(void) print {
printf( "%_f + %_fi", real, imaginary );
}
@end
main.m
#import <stdio.h>
#import "Fraction.h"
#import "Complex.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac = [[Fraction alloc]
initWithNumerator: 3 denominator: 10];
Complex *comp = [[Complex alloc]
initWithReal: 5 andImaginary: 15];
id <Printing> printable;
id <NSCopying, Printing> copyPrintable;
// print it
printable = frac;
printf( "The fraction is: " );
[printable print];
printf( "\n" );
// print complex
printable = comp;
printf( "The complex number is: " );
[printable print];
printf( "\n" );
// this compiles because Fraction comforms
to both Printing and NSCopyable
copyPrintable = frac;
// this doesn't compile because Complex
only conforms to Printing
//copyPrintable = comp;
// test conformance
// true
if ( [frac conformsToProtocol:
@protocol( NSCopying )] == YES ) {
printf( "Fraction conforms to
NSCopying\n" );
}
// false
if ( [comp conformsToProtocol:
@protocol( NSCopying )] == YES ) {
printf( "Complex conforms to
NSCopying\n" );
}
// free memory
[frac release];
[comp release];
return 0;
}
output
The fraction is: 3/10
The complex number is: 5.000000 + 15.000000i
Fraction conforms to NSCopying
• Memory Management
o Up until now I've kind of dodged memory management in
Objective-C. Sure you can call dealloc on an object, but what happens
if the object contains pointers to other objects? One has to be
concerned about freeing the memory of those objects as well. Also
how does the Foundation framework manage memory when you create
classes from it? This will all be explained.
o Note: everything up until this point has been properly memory
managed, incase you're wondering.
o Retain and Release
Retain and release are two methods inherited from any
object that has NSObject as a parent. Each object has an
internal counter that can be used to keep track of the number
references an object has. So if you have 3 referneces, you don't
want to dealloc yourself. However once you reach 0, you
should dealloc yourself. [object retain] increments the counter
by 1 (which starts at 1) and [object release] decrements it by 1.
If the [object release] invocation causes the count to reach 0,
dealloc is then called.
Fraction.m
...
-(void) dealloc {
printf( "Deallocing fraction\n" );
[super dealloc];
}
...
output
Fraction 1 retain count: 1
Fraction 2 retain count: 1
Fraction 1 retain count: 3
Fraction 2 retain count: 2
Fraction 1 retain count: 2
Fraction 2 retain count: 1
Deallocing fraction
Deallocing fraction
AddressCard.m
#import "AddressCard.h"
#import <stdio.h>
@implementation AddressCard
-(AddressCard*) initWithFirst: (NSString*) f
last: (NSString*) l
email: (NSString*) e {
self = [super init];
if ( self ) {
[self setFirst: f last: l email: e];
}
return self;
}
-(NSString*) first {
return first;
}
-(NSString*) last {
return last;
}
-(NSString*) email {
return email;
}
-(void) setFirst: (NSString*) f {
[f retain];
[first release];
first = f;
}
-(void) setLast: (NSString*) l {
[l retain];
[last release];
last = l;
}
-(void) setEmail: (NSString*) e {
[e retain];
[email release];
email = e;
}
-(void) setFirst: (NSString*) f
last: (NSString*) l
email: (NSString*) e {
[self setFirst: f];
[self setLast: l];
[self setEmail: e];
}
-(void) setFirst: (NSString*) f last:
(NSString*) l {
[self setFirst: f];
[self setLast: l];
}
-(void) print {
printf( "%s %s <%s>", [first cString],
[last
cString],
[email
cString] );
}
-(void) dealloc {
[first release];
[last release];
[email release];
[super dealloc];
}
@end
main.m
#import "AddressCard.h"
#import <Foundation/NSString.h>
#import <stdio.h>
int main( int argc, const char *argv[] ) {
NSString *first =[[NSString alloc]
initWithCString: "Tom"];
NSString *last = [[NSString alloc]
initWithCString: "Jones"];
NSString *email = [[NSString alloc]
initWithCString: "[email protected]"];
AddressCard *tom = [[AddressCard alloc]
initWithFirst: first
la
st: last
em
ail: email];
// we're done with the strings, so we must
dealloc them
[first release];
[last release];
[email release];
// print to show the retain count
printf( "Retain count: %i\n", [[tom first]
retainCount] );
[tom print];
printf( "\n" );
// free memory
[tom release];
return 0;
}
output
Retain count: 1
Tom Jones <[email protected]>
output
constant string retain count: ffffffff
string managed by the pool retain count: 1
self managed string retain count: 1
If you run this you'll notice a few things. One is that the
retainCount of str1 is ffffffff.
The other is, I only release str3, yet this program is
memory management perfect. The reason is the first constant
string is added to the autorelease pool automatically. The other
string is made using stringWithString. This method creates a
string that is owned by NSString class, which also puts it in the
auto release pool.
It's important to remember, for proper memory
management, that convience methods like [NSString
stringWithString: @"String"] use autorelease pools, but alloc
methods like [[NSString alloc] initWithString: @"String"] do
not use autorelease pools for managing memory.
There are two ways to manage memory in Objective-C:
1) retain and release or 2) retain and release/autorelease.
For each retain, there must be one release OR one
autorelease.
The following example shows what I mean by this
Based on an example in "Programming in Objective-C,"
Copyright © 2004 by Sams Publishing. Used with permission
Fraction.h
...
+(Fraction*) fractionWithNumerator: (int) n
denominator: (int) d;
...
Fraction.m
...
+(Fraction*) fractionWithNumerator: (int) n
denominator: (int) d {
Fraction *ret = [[Fraction alloc]
initWithNumerator: n denominator: d];
[ret autorelease];
return ret;
}
...
main.m
#import <Foundation/NSAutoreleasePool.h>
#import "Fraction.h"
#import <stdio.h>
int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool =
[[NSAutoreleasePool alloc] init];
Fraction *frac1 = [Fraction
fractionWithNumerator: 2 denominator: 5];
Fraction *frac2 = [Fraction
fractionWithNumerator: 1 denominator: 3];
// print frac 1
printf( "Fraction 1: " );
[frac1 print];
printf( "\n" );
// print frac 2
printf( "Fraction 2: " );
[frac2 print];
printf( "\n" );
// this causes a segmentation fault
//[frac1 release];
// release the pool and all objects in it
[pool release];
return 0;
}
output
Fraction 1: 2/5
Fraction 2: 1/3
output
----static array
Me
Myself
I
----mutable array
One
Two
Me
Myself
I
Three
----sorted mutable array
I
Me
Myself
One
Three
Two
output
----static dictionary
1 => one
2 => two
3 => three
----mutable dictionary
[email protected] => Bob
[email protected] => Tom
• Pros and Cons
o Pros
Cateogies
Posing
Dynamic typing
Pointer counting
Flexible message passing
Not an overly complex extention to C
Can interface with C++ via Objective-C++
o Cons
No namespaces
No operator overloading (this is often considered a Pro
though, but operator overloading used properly can reduce
code clutter)
Still some cruft in language, although no more than C+
+
• More Information
o Programming in Objective-C 2.0 (2nd Edition)
o Programming in Objective-C
o Learning Cocoa with Objective-C
o Cocoa Programming for Mac OS X
o Object-Oriented Programming and the Objective-C Language
o GNUstep mini tutorials