SlideShare una empresa de Scribd logo
1 de 34
Descargar para leer sin conexión
24/11/10 21:11Objective-C Beginner's Guide
Página 1 de 34http://www.otierney.net/objective-c.html
Translations: English | Chinese | Korean
Outline
Getting Started
Downloading this tutorial
Setting up the environment
Preamble
Making hello world
Creating Classes
@interface
@implementation
Piecing it together
The Details...
Multiple Parameters
Constructors
Access Privledges
Class level access
Exceptions
Inheritance, Polymorphism, and other OOP features
The id type
Inheritance
Dynamic types
Categories
Posing
Protocols
Memory Management
Retain and Release
Dealloc
Autorelease Pool
Foundation Framework Classes
NSArray
NSDictionary
Pros and Cons
More Information
Getting Started
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.
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
24/11/10 21:11Objective-C Beginner's Guide
Página 2 de 34http://www.otierney.net/objective-c.html
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
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.
Making hello world
hello.m
#import <stdio.h>
int main( int argc, const char *argv[] ) {
printf( "hello worldn" );
return 0;
}
output
hello world
You use #import instead of #include in Objective-C
The default file extention for Objective-C is .m
24/11/10 21:11Objective-C Beginner's Guide
Página 3 de 34http://www.otierney.net/objective-c.html
Creating classes
@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
@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;
}
24/11/10 21:11Objective-C Beginner's Guide
Página 4 de 34http://www.otierney.net/objective-c.html
-(int) numerator {
return numerator;
}
@end
@implementation ClassName starts the implementation @end ends it
All the defined methods are implemented very simlar to how they are declared in the
interface
Piecing it together
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
main.m
#import <stdio.h>
#import "Fraction.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac = [[Fraction alloc] init];
// set the values
[frac setNumerator: 1];
[frac setDenominator: 3];
// print it
printf( "The fraction is: " );
[frac print];
printf( "n" );
// free memory
[frac release];
return 0;
}
output
The fraction is: 1/3
Fraction *frac = [[Fraction alloc] init];
There are several important things in this one line.
The way methods in Objective-C are called is [object method], which is similar
to object->method() in C++
Objective-C doesn't have value types, so there is nothing similar to C++'s:
Fraction frac; frac.print();. You always deal with objects as pointers in
Objective-C.
What this line is really doing is two things: [Fraction alloc] is calling the alloc
method on the Fraction class. This is similar to mallocing memory, because that
is all that is done in this operation.
[object init] is the constructor call, which initializes any variables in the object.
This method is called on the instance returned from [Fraction alloc]. This
operation is so common it's usually just done in one line as Object *var =
[[Object alloc] init];
[frac setNumerator: 1] is quite simple. It's calling the setNumerator method on frac,
and passing it the parameter 1.
Like every c variant, there's a construct for freeing memory. This is done via release,
which is inherited from NSObject. This method will be explainted in greater detail
24/11/10 21:11Objective-C Beginner's Guide
Página 5 de 34http://www.otierney.net/objective-c.html
later.
The details...
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
24/11/10 21:11Objective-C Beginner's Guide
Página 6 de 34http://www.otierney.net/objective-c.html
The fraction is: 1/3
Fraction 2 is: 1/5
The method is actually called setNumerator:andDenominator:
Additional parameters are added the same was as the 2nd, such that you'd have
method:label1:label2:label3: and you'd call it with [obj method: param1 label1:
param2 label2: param3 label3: param4]
Labels are optional. It's possible to have a method named method:::. This is done by
simply not specifing label names, but just a : to separate the parameters. This is
however not advised.
Constructors
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
Fraction.h
...
-(Fraction*) initWithNumerator: (int) n denominator: (int) d;
...
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: " );
24/11/10 21:11Objective-C Beginner's Guide
Página 7 de 34http://www.otierney.net/objective-c.html
[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
@interface declaration is identical to a regular function
@implementation shows a new keyword: super
Similar to Java, Objective-C only has one parent class.
Accessing it's super constructor is done through [super init] and this is required
for proper inheritance.
This returns an instance which you assign to another new keyword, self. Self is
similar to this in Java and C++.
if ( self ) is the same as if ( self != nil ) to make sure that the super constructor
successfully returned a new object. nil is Objective-C's form of NULL from C/C++.
This is gotten from including NSObject.
After you've initialized the varialbes, you return yourself with return self;
The deafult constructor is -(id) init;
Constructors in Objective-C are technically just "init" methods, they aren't a special
construct like they are in C++ and Java.
Access Privledges
The default access is @protected
Java implements this with public/private/protected modifiers infront of methods and
variables. Objective-C's approach is much more similar to C++'s for instance variables
Access.h
#import <Foundation/NSObject.h>
@interface Access: NSObject {
@public
int publicVar;
@private
int privateVar;
int privateVar2;
@protected
int protectedVar;
}
@end
Access.m
#import "Access.h"
@implementation Access
@end
main.m
24/11/10 21:11Objective-C Beginner's Guide
Página 8 de 34http://www.otierney.net/objective-c.html
#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: %in", a->publicVar );
// doesn't compile
//a->privateVar = 10;
//printf( "private var: %in", 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.
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"
24/11/10 21:11Objective-C Beginner's Guide
Página 9 de 34http://www.otierney.net/objective-c.html
#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: %in", [ClassA initCount] );
ClassA *c3 = [[ClassA alloc] init];
// print count again
printf( "ClassA count: %in", [ClassA initCount] );
[c1 release];
[c2 release];
[c3 release];
return 0;
}
output
ClassA count: 2
ClassA count: 3
static int count = 0; This is how the class variable is declared. This is not the ideal
place for such a variable. A nicer solution would have been like Java's implementation
of static class variables. However this works
+(int) initCount; This is the actual method that returns the count. Notice the subtle
difference. Instead of using a - infront of the type, a + is used. The + denotes a class
level function.
Accessing the variable is no different than member variables, as seen by count++ in
the constructor of ClassA.
The +(void) initialize method is called when Objective-C starts your program, and it's
called for every class. This is a good place to initialize class level variables like our
count.
Exceptions
NOTE: Exception handling is only supported in Mac OS X 10.3
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
CupWarningException.h
#import <Foundation/NSException.h>
@interface CupWarningException: NSException
@end
CupWarningException.m
#import "CupWarningException.h"
@implementation CupWarningException
@end
CupOverflowException.h
#import <Foundation/NSException.h>
24/11/10 21:11Objective-C Beginner's Guide
Página 10 de 34http://www.otierney.net/objective-c.html
@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 ) {
24/11/10 21:11Objective-C Beginner's Guide
Página 11 de 34http://www.otierney.net/objective-c.html
// 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: %in", 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: %sn", [[e name] cString], [[e reason] cString] );
}
// free memory
[cup release];
[pool release];
}
24/11/10 21:11Objective-C Beginner's Guide
Página 12 de 34http://www.otierney.net/objective-c.html
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
NSAutoreleasePool is a memory management class. Don't worry about what this does
right now.
Exceptions that are thrown don't have to extend NSException. You can just as easily
use an id as well: @catch ( id e ) { ... }
There is also a finally block, which behaves just like Java's. The contents of a finally
block are guaranteed to be called.
The string as show in Cup.m, @"CupOverflowException", is a constant NSString
object. The @ sign is used often in Objective-C to denote extentions to the language.
A C string is just like C and C++, "String constant", and is of type char *.
Inheritance, Polymorphism, and other OOP
features
The id type
Objective-C has a type called id, that acts in some ways like a void*, though it's meant
strictly for objects. Objective-C differs from Java and C++ in that when you call a
method on an object, it doesn't need to know the type. That method simply just has to
exist. This is refered to as message pasing in Objective-C.
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;
}
-(Fraction*) initWithNumerator: (int) n denominator: (int) d;
-(void) print;
-(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>
24/11/10 21:11Objective-C Beginner's Guide
Página 13 de 34http://www.otierney.net/objective-c.html
@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 ) {
24/11/10 21:11Objective-C Beginner's Guide
Página 14 de 34http://www.otierney.net/objective-c.html
[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
24/11/10 21:11Objective-C Beginner's Guide
Página 15 de 34http://www.otierney.net/objective-c.html
The complex number is: 10.000000 + 15.000000i
There are obvious benefits to this type of dynamic binding. You don't have to know
the type of something to call a method on it. If the object responds to a message, it
will invoke that method. Lots of nasty casting isn't involved in this either, such as in
Java to call .intValue() on an integer object would involve casting first, then calling
the method.
Inheritance
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
Rectangle.h
#import <Foundation/NSObject.h>
@interface Rectangle: NSObject {
int width;
int height;
}
-(Rectangle*) initWithWidth: (int) w height: (int) h;
-(void) setWidth: (int) w;
-(void) setHeight: (int) h;
-(void) setWidth: (int) w height: (int) h;
-(int) width;
-(int) height;
-(void) print;
@end
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 {
24/11/10 21:11Objective-C Beginner's Guide
Página 16 de 34http://www.otierney.net/objective-c.html
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" );
24/11/10 21:11Objective-C Beginner's Guide
Página 17 de 34http://www.otierney.net/objective-c.html
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
Inheritance in Objective-C is similar to Java. When you extend your super class (of
which you can only have one parent) you can override the methods of your super
class by simply putting the new implementations in the child classes implementation.
No fooling with virtual tables like C++.
One thing left out here that is worth nothing is what would happen if you attempted to
call the constructor for rectangle like: Square *sq = [[Square alloc] initWithWidth: 10
height: 15]. The answer is it will throw a compile error. Since the return type of the
rectangle constructor is Rectangle*, not Square* this would not work. In such a case if
you want this to occur, that's what the id variable is good for. Just change the
Rectangle* return type to id if you wish to use your parent's constructors in a subclass.
Dynamic types
There are several methods for working with dynamic types in Objective-C
-(BOOL) isKindOfClass: classObj
is object a descendent or
member of classObj
-(BOOL) isMemberOfClass: classObj is object a member of classObj
-(BOOL) respondsToSelector: selector
does the object have a method
named specifiec by the selector
+(BOOL) instancesRespondToSelector:
selector
does an object created by this
class have the ability to respond
to the specified selector
-(id) performSelector: selector
invoke the specified selector on
the object
Every object inherited from NSObject has a class method that returns a class object.
This is very similar to Java's getClass() method. This class object is used in the
methods above.
Selectors are used to represent a message in Objective-C. The syntax for creating a
selector is shown in the next example
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
main.m
#import "Square.h"
#import "Rectangle.h"
24/11/10 21:11Objective-C Beginner's Guide
Página 18 de 34http://www.otierney.net/objective-c.html
#import <stdio.h>
int main( int argc, const char *argv[] ) {
Rectangle *rec = [[Rectangle alloc] initWithWidth: 10 height: 20];
Square *sq = [[Square alloc] initWithSize: 15];
// isMemberOfClass
// true
if ( [sq isMemberOfClass: [Square class]] == YES ) {
printf( "square is a member of square classn" );
}
// false
if ( [sq isMemberOfClass: [Rectangle class]] == YES ) {
printf( "square is a member of rectangle classn" );
}
// false
if ( [sq isMemberOfClass: [NSObject class]] == YES ) {
printf( "square is a member of object classn" );
}
// isKindOfClass
// true
if ( [sq isKindOfClass: [Square class]] == YES ) {
printf( "square is a kind of square classn" );
}
// true
if ( [sq isKindOfClass: [Rectangle class]] == YES ) {
printf( "square is a kind of rectangle classn" );
}
// true
if ( [sq isKindOfClass: [NSObject class]] == YES ) {
printf( "square is a kind of object classn" );
}
// respondsToSelector
// true
if ( [sq respondsToSelector: @selector( setSize: )] == YES ) {
printf( "square responds to setSize: methodn" );
}
// false
if ( [sq respondsToSelector: @selector( nonExistant )] == YES ) {
printf( "square responds to nonExistant methodn" );
}
// true
if ( [Square respondsToSelector: @selector( alloc )] == YES ) {
printf( "square class responds to alloc methodn" );
}
// instancesRespondToSelector
// false
if ( [Rectangle instancesRespondToSelector: @selector( setSize: )] == YES ) {
printf( "rectangle instance responds to setSize: methodn" );
}
// true
if ( [Square instancesRespondToSelector: @selector( setSize: )] == YES ) {
printf( "square instance responds to setSize: methodn" );
}
24/11/10 21:11Objective-C Beginner's Guide
Página 19 de 34http://www.otierney.net/objective-c.html
// free memory
[rec release];
[sq release];
return 0;
}
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
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]
24/11/10 21:11Objective-C Beginner's Guide
Página 20 de 34http://www.otierney.net/objective-c.html
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
1/3 * 2/5 = 2/15
The magic here is the two @implementation and @interface lines: @interface Fraction
(Math) and @implementation Fraction (Math).
There can only be one category with the same name. Additional cateogies may be
added on with different but unqiue names.
Categories can't add instance variables.
Categories are useful for creating private methods. Since Objective-C has no notion of
private/protected/public methods like java does, one has to create categories that hide
such functionality. The way this is done is by moving the private methods from your
class's header (.h) file to the implementation file (.m). The following is a very brief
example of what I mean.
MyClass.h
#import <Foundation/NSObject.h>
@interface MyClass: NSObject
-(void) publicMethod;
@end
MyClass.m
#import "MyClass.h"
#import <stdio.h>
@implementation MyClass
-(void) publicMethod {
printf( "public methodn" );
}
24/11/10 21:11Objective-C Beginner's Guide
Página 21 de 34http://www.otierney.net/objective-c.html
@end
// private methods
@interface MyClass (Private)
-(void) privateMethod;
@end
@implementation MyClass (Private)
-(void) privateMethod {
printf( "private methodn" );
}
@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
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 );
}
24/11/10 21:11Objective-C Beginner's Guide
Página 22 de 34http://www.otierney.net/objective-c.html
@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)
The output from this program would print the first fraction s 3/10. The second would
output (3/10), which is implemented by FractionB.
The method poseAsClass is part of NSObject. This allows a subclass to pose as a
superclass.
Protocols
A Protocol in Objective-C is identical in functionality to an interface in Java, or a
purely virtual class in C++.
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
Printing.h
@protocol Printing
-(void) print;
@end
Fraction.h
#import <Foundation/NSObject.h>
#import "Printing.h"
@interface Fraction: NSObject <Printing, NSCopying> {
int numerator;
24/11/10 21:11Objective-C Beginner's Guide
Página 23 de 34http://www.otierney.net/objective-c.html
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
denominator: denominator];
}
@end
Complex.h
#import <Foundation/NSObject.h>
#import "Printing.h"
@interface Complex: NSObject <Printing> {
double real;
double imaginary;
}
24/11/10 21:11Objective-C Beginner's Guide
Página 24 de 34http://www.otierney.net/objective-c.html
-(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: " );
24/11/10 21:11Objective-C Beginner's Guide
Página 25 de 34http://www.otierney.net/objective-c.html
[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 NSCopyingn" );
}
// false
if ( [comp conformsToProtocol: @protocol( NSCopying )] == YES ) {
printf( "Complex conforms to NSCopyingn" );
}
// 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
The protocol specification is quite simple. it is basically @protocol ProtocolName
(methods you must implement) @end.
To conform to a protocol, you put the protocols you're conforming to in <>'s, and
comma separate them. Example: @interface SomeClass <Protocol1, Protocol2,
Protocol3>
The methods that the protocol requires to be implemented are not required to be in the
list of methods for the header file. As you can see, Complex.h doesn't have a
definition for -(void) print, but it still implements it since it conforms to the protocol.
One unique aspect of Objective-C's interface system is how you specify types. Rather
than specifying it like Java or C++ as: Printing *someVar = ( Printing * ) frac; for
example, you use the id type with a restricted protocol: id <Printing> var = frac; This
allows you to dynamically specify a type that requires multiple protocols, all with one
variable. Such as: id <Printing, NSCopying> var = frac;
Much like using @selector for testing an object's inheritance, you can use @protocol
to test for conformance of interfaces. [object conformsToProtocol: @protocol(
SomeProtocol )] returns a BOOL if the object conforms to that protocol. This works
the same for classes as well: [SomeClass conformsToProtocol: @protocol(
SomeProtocol )].
Memory Management
24/11/10 21:11Objective-C Beginner's Guide
Página 26 de 34http://www.otierney.net/objective-c.html
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.
Note: everything up until this point has been properly memory managed, incase you're
wondering.
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 fractionn" );
[super dealloc];
}
...
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
main.m
#import "Fraction.h"
#import <stdio.h>
int main( int argc, const char *argv[] ) {
Fraction *frac1 = [[Fraction alloc] init];
Fraction *frac2 = [[Fraction alloc] init];
// print current counts
printf( "Fraction 1 retain count: %in", [frac1 retainCount] );
printf( "Fraction 2 retain count: %in", [frac2 retainCount] );
// increment them
[frac1 retain]; // 2
[frac1 retain]; // 3
[frac2 retain]; // 2
// print current counts
printf( "Fraction 1 retain count: %in", [frac1 retainCount] );
printf( "Fraction 2 retain count: %in", [frac2 retainCount] );
// decrement
[frac1 release]; // 2
[frac2 release]; // 1
// print current counts
printf( "Fraction 1 retain count: %in", [frac1 retainCount] );
printf( "Fraction 2 retain count: %in", [frac2 retainCount] );
// release them until they dealloc themselves
[frac1 release]; // 1
[frac1 release]; // 0
[frac2 release]; // 0
24/11/10 21:11Objective-C Beginner's Guide
Página 27 de 34http://www.otierney.net/objective-c.html
}
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
The retain calls increment the counter. The release calls decrement it. One can get the
count as an int by calling [obj retainCount]. Once the retainCount reaches 0, both
objects dealloc themselves and you can see this when both print out "Deallocing
fraction."
Dealloc
When your object contains other objects, you must free them whenever you yourself
dealloc. One of the nice advantages to Objective-C is you can pass messages to nil, so
there isn't a lot of error checking to release an object.
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
AddressCard.h
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
@interface AddressCard: NSObject {
NSString *first;
NSString *last;
NSString *email;
}
-(AddressCard*) initWithFirst: (NSString*) f
last: (NSString*) l
email: (NSString*) e;
-(NSString*) first;
-(NSString*) last;
-(NSString*) email;
-(void) setFirst: (NSString*) f;
-(void) setLast: (NSString*) l;
-(void) setEmail: (NSString*) e;
-(void) setFirst: (NSString*) f
last: (NSString*) l
email: (NSString*) e;
-(void) setFirst: (NSString*) f last: (NSString*) l;
-(void) print;
@end
AddressCard.m
#import "AddressCard.h"
#import <stdio.h>
@implementation AddressCard
-(AddressCard*) initWithFirst: (NSString*) f
last: (NSString*) l
email: (NSString*) e {
self = [super init];
24/11/10 21:11Objective-C Beginner's Guide
Página 28 de 34http://www.otierney.net/objective-c.html
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
24/11/10 21:11Objective-C Beginner's Guide
Página 29 de 34http://www.otierney.net/objective-c.html
#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: "tom@jones.com"];
AddressCard *tom = [[AddressCard alloc] initWithFirst: first
last: last
email: 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: %in", [[tom first] retainCount] );
[tom print];
printf( "n" );
// free memory
[tom release];
return 0;
}
output
Retain count: 1
Tom Jones <tom@jones.com>
This example shows not only how to make a dealloc method, as shown in
AddressCard.m, but one way to do member variables.
The order of the 3 operations in each set method is very important. Lets say you'return
passing a parameter of yourself to one of your methods (a bit of an odd example, but
this can happen). If you release first, THEN retain you will destruct yourself! That's
why you should always 1) retain 2) release 3) set the value.
Normally one wouldn't initialize variables with C strings because they don't support
unicode. The next example, with NSAutoreleasePool shows the proper way to do
strings and initializing.
This is just one way of handling member variable memory management. One way to
handle this is to create copies inside your set methods.
Autorelease Pool
When you want to start doing more programming using NSString and other
Foundation framework classes you need a more flexible system. This system is using
Autorelease pools.
When developing Mac Cocoa applications, the auto release pool is setup automatically
for you.
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
main.m
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import <stdio.h>
int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
24/11/10 21:11Objective-C Beginner's Guide
Página 30 de 34http://www.otierney.net/objective-c.html
NSString *str1 = @"constant string";
NSString *str2 = [NSString stringWithString: @"string managed by the pool"];
NSString *str3 = [[NSString alloc] initWithString: @"self managed string"];
// print the strings
printf( "%s retain count: %xn", [str1 cString], [str1 retainCount] );
printf( "%s retain count: %xn", [str2 cString], [str2 retainCount] );
printf( "%s retain count: %xn", [str3 cString], [str3 retainCount] );
// free memory
[str3 release];
// free pool
[pool release];
return 0;
}
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[] ) {
24/11/10 21:11Objective-C Beginner's Guide
Página 31 de 34http://www.otierney.net/objective-c.html
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
In this example, the method is a class level method. After the object is created,
autorelease is called on it. Inside the body of the main method, I never call release on
the object.
The reason this works is because: for every retain, one release or autorelease must be
called. The object's retain count starts out as 1, and I called autorelease on it once.
This means 1 - 1 = 0. Once the autorelease pool is released, it counts the autorelease
calls on all objects and decrements them with [obj release] with the same number of
times autorelease was called per object.
As the comment says, uncommenting that line causes a segment fault. Since
autorelease was already called on the object, calling release on it, and then releasing
the autorelease pool would attempt to call dealloc on an object that is nil, which is not
valid. The end math is 1 (creation) - 1 (release) - 1 (autorelease) = -1.
Auto release pools can be dynamically created for large amounts of temporary objects.
All one must do is create a pool, perform any large chunk of code that creates lots of
temporary objects, then release the pool. As you may wonder, it this means it is
possible to have more than one auto release pool at a time.
Foundation framework classes
The Foundation framework is similar to C++'s Standard Template Library. Although since
Objective-C has real dynamic types, the horrible cludge that is C++'s templating is not
necessary. This framework contains collections, networking, threading, and much more.
NSArray
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
main.m
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
24/11/10 21:11Objective-C Beginner's Guide
Página 32 de 34http://www.otierney.net/objective-c.html
#import <Foundation/NSEnumerator.h>
#import <stdio.h>
void print( NSArray *array ) {
NSEnumerator *enumerator = [array objectEnumerator];
id obj;
while ( obj = [enumerator nextObject] ) {
printf( "%sn", [[obj description] cString] );
}
}
int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *arr = [[NSArray alloc] initWithObjects:
@"Me", @"Myself", @"I", nil];
NSMutableArray *mutable = [[NSMutableArray alloc] init];
// enumerate over items
printf( "----static arrayn" );
print( arr );
// add stuff
[mutable addObject: @"One"];
[mutable addObject: @"Two"];
[mutable addObjectsFromArray: arr];
[mutable addObject: @"Three"];
// print em
printf( "----mutable arrayn" );
print( mutable );
// sort then print
printf( "----sorted mutable arrayn" );
[mutable sortUsingSelector: @selector( caseInsensitiveCompare: )];
print( mutable );
// free memory
[arr release];
[mutable release];
[pool release];
return 0;
}
output
----static array
Me
Myself
I
----mutable array
One
Two
Me
Myself
I
Three
----sorted mutable array
I
Me
Myself
One
Three
Two
There are two kinds of arrays (and of usually most data oriented Foundation classes)
24/11/10 21:11Objective-C Beginner's Guide
Página 33 de 34http://www.otierney.net/objective-c.html
NSArray and NSMutableArray. As the name suggests, Mutable is changable, NSArray
then is not. This means you can make an NSArray but once you have you can't change
the length.
You initialize an array via the constructor using Obj, Obj, Obj, ..., nil. The nil is an
ending delimiter.
The sorting shows how to sort an object using a selector. The selector tells the array to
sort using NSString's case insensitive compare. If your object has several sort
methods, you can choose anyone you want using this selector.
In the print method, I used the method description. This is similar to Java's toString. It
returns an NSString representation of an object.
NSEnumerator is similar to Java's enumerator system. The reason why while ( obj =
[array objectEnumerator] ) works is because objectEnumerator returns nil on the last
object. Since in C nil is usually 0, this is the same as false. ( ( obj = [array
objectEnumerator] ) != nil ) might be preferable
NSDictionary
Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams
Publishing. Used with permission
main.m
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/Foundation.h<
#import <stdio.h>
void print( NSDictionary *map ) {
NSEnumerator *enumerator = [map keyEnumerator];
id key;
while ( key = [enumerator nextObject] ) {
printf( "%s => %sn",
[[key description] cString],
[[[map objectForKey: key] description] cString] );
}
}
int main( int argc, const char *argv[] ) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
@"one", [NSNumber numberWithInt: 1],
@"two", [NSNumber numberWithInt: 2],
@"three", [NSNumber numberWithInt: 3],
nil];
NSMutableDictionary *mutable = [[NSMutableDictionary alloc] init];
// print dictionary
printf( "----static dictionaryn" );
print( dictionary );
// add objects
[mutable setObject: @"Tom" forKey: @"tom@jones.com"];
[mutable setObject: @"Bob" forKey: @"bob@dole.com" ];
// print mutable dictionary
printf( "----mutable dictionaryn" );
print( mutable );
// free memory
[dictionary release];
[mutable release];
24/11/10 21:11Objective-C Beginner's Guide
Página 34 de 34http://www.otierney.net/objective-c.html
[pool release];
return 0;
}
output
----static dictionary
1 => one
2 => two
3 => three
----mutable dictionary
bob@dole.com => Bob
tom@jones.com => Tom
Pros and Cons
Pros
Cateogies
Posing
Dynamic typing
Pointer counting
Flexible message passing
Not an overly complex extention to C
Can interface with C++ via Objective-C++
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
Programming in Objective-C 2.0 (2nd Edition)
Programming in Objective-C
Learning Cocoa with Objective-C
Cocoa Programming for Mac OS X
Object-Oriented Programming and the Objective-C Language
GNUstep mini tutorials
Last modified: April 13, 2004.

Más contenido relacionado

La actualidad más candente

C++ 11 Features
C++ 11 FeaturesC++ 11 Features
C++ 11 FeaturesJan Rüegg
 
OOP in C - Inherit (Chinese Version)
OOP in C - Inherit (Chinese Version)OOP in C - Inherit (Chinese Version)
OOP in C - Inherit (Chinese Version)Kai-Feng Chou
 
TDD in C - Recently Used List Kata
TDD in C - Recently Used List KataTDD in C - Recently Used List Kata
TDD in C - Recently Used List KataOlve Maudal
 
C++ vs C#
C++ vs C#C++ vs C#
C++ vs C#sudipv
 
Object-Oriented Programming in Modern C++. Borislav Stanimirov. CoreHard Spri...
Object-Oriented Programming in Modern C++. Borislav Stanimirov. CoreHard Spri...Object-Oriented Programming in Modern C++. Borislav Stanimirov. CoreHard Spri...
Object-Oriented Programming in Modern C++. Borislav Stanimirov. CoreHard Spri...corehard_by
 
OOP in C - Before GObject (Chinese Version)
OOP in C - Before GObject (Chinese Version)OOP in C - Before GObject (Chinese Version)
OOP in C - Before GObject (Chinese Version)Kai-Feng Chou
 
2. data, operators, io
2. data, operators, io2. data, operators, io
2. data, operators, iohtaitk
 
Programming Fundamentals lecture 5
Programming Fundamentals lecture 5Programming Fundamentals lecture 5
Programming Fundamentals lecture 5REHAN IJAZ
 
OOPS using C++
OOPS using C++OOPS using C++
OOPS using C++cpjcollege
 
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...Francesco Casalegno
 
C++ Interface Versioning
C++ Interface VersioningC++ Interface Versioning
C++ Interface VersioningSkills Matter
 

La actualidad más candente (20)

Deep C
Deep CDeep C
Deep C
 
C
CC
C
 
C tutorial
C tutorialC tutorial
C tutorial
 
2. data, operators, io
2. data, operators, io2. data, operators, io
2. data, operators, io
 
C Programming Tutorial - www.infomtec.com
C Programming Tutorial - www.infomtec.comC Programming Tutorial - www.infomtec.com
C Programming Tutorial - www.infomtec.com
 
C introduction by piyushkumar
C introduction by piyushkumarC introduction by piyushkumar
C introduction by piyushkumar
 
C++ 11 Features
C++ 11 FeaturesC++ 11 Features
C++ 11 Features
 
OOP in C - Inherit (Chinese Version)
OOP in C - Inherit (Chinese Version)OOP in C - Inherit (Chinese Version)
OOP in C - Inherit (Chinese Version)
 
TDD in C - Recently Used List Kata
TDD in C - Recently Used List KataTDD in C - Recently Used List Kata
TDD in C - Recently Used List Kata
 
C++ vs C#
C++ vs C#C++ vs C#
C++ vs C#
 
Object-Oriented Programming in Modern C++. Borislav Stanimirov. CoreHard Spri...
Object-Oriented Programming in Modern C++. Borislav Stanimirov. CoreHard Spri...Object-Oriented Programming in Modern C++. Borislav Stanimirov. CoreHard Spri...
Object-Oriented Programming in Modern C++. Borislav Stanimirov. CoreHard Spri...
 
OOP in C - Before GObject (Chinese Version)
OOP in C - Before GObject (Chinese Version)OOP in C - Before GObject (Chinese Version)
OOP in C - Before GObject (Chinese Version)
 
2. data, operators, io
2. data, operators, io2. data, operators, io
2. data, operators, io
 
88 c-programs
88 c-programs88 c-programs
88 c-programs
 
Programming Fundamentals lecture 5
Programming Fundamentals lecture 5Programming Fundamentals lecture 5
Programming Fundamentals lecture 5
 
OOPS using C++
OOPS using C++OOPS using C++
OOPS using C++
 
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
 
C++ Interface Versioning
C++ Interface VersioningC++ Interface Versioning
C++ Interface Versioning
 
C++ Presentation
C++ PresentationC++ Presentation
C++ Presentation
 
UNIT-II CP DOC.docx
UNIT-II CP DOC.docxUNIT-II CP DOC.docx
UNIT-II CP DOC.docx
 

Destacado

Marta Leo Smau16
Marta Leo Smau16Marta Leo Smau16
Marta Leo Smau16Marta Leo
 
Детские порталы. Особенности поведения детской аудитории
Детские порталы. Особенности поведения детской аудиторииДетские порталы. Особенности поведения детской аудитории
Детские порталы. Особенности поведения детской аудиторииОлег Муковозов
 
LowCountryVolkswagen.com_AAA Cell Phones And Driving Research Update
LowCountryVolkswagen.com_AAA Cell Phones And Driving Research UpdateLowCountryVolkswagen.com_AAA Cell Phones And Driving Research Update
LowCountryVolkswagen.com_AAA Cell Phones And Driving Research UpdateLowCountry Volkswagen
 
Ipad in education 2
Ipad in education 2Ipad in education 2
Ipad in education 2Amy Cantone
 
Mobile 101 Class 4: Mobile Measurement
Mobile 101 Class 4: Mobile MeasurementMobile 101 Class 4: Mobile Measurement
Mobile 101 Class 4: Mobile MeasurementThe Media Kitchen
 
Профессиональный стандарт специалиста по связям с общественностью
Профессиональный стандарт специалиста по связям с общественностьюПрофессиональный стандарт специалиста по связям с общественностью
Профессиональный стандарт специалиста по связям с общественностьюОлег Муковозов
 
LowCountryVolkswagen.com_AAA Aggressive Driving Research Update
LowCountryVolkswagen.com_AAA Aggressive Driving Research UpdateLowCountryVolkswagen.com_AAA Aggressive Driving Research Update
LowCountryVolkswagen.com_AAA Aggressive Driving Research UpdateLowCountry Volkswagen
 
The Media Kitchen - 2014 Digital Innovation Currents
The Media Kitchen - 2014 Digital Innovation CurrentsThe Media Kitchen - 2014 Digital Innovation Currents
The Media Kitchen - 2014 Digital Innovation CurrentsThe Media Kitchen
 
Отраслевой доклад 2016. Полиграфия России
Отраслевой доклад 2016. Полиграфия РоссииОтраслевой доклад 2016. Полиграфия России
Отраслевой доклад 2016. Полиграфия РоссииОлег Муковозов
 
Отраслевой доклад 2015. Печатные СМИ в России
Отраслевой доклад 2015. Печатные СМИ в РоссииОтраслевой доклад 2015. Печатные СМИ в России
Отраслевой доклад 2015. Печатные СМИ в РоссииОлег Муковозов
 
Семейный бизнес в России. Информационно-аналитическая справка, янв. 2011
Семейный бизнес в России. Информационно-аналитическая справка, янв. 2011Семейный бизнес в России. Информационно-аналитическая справка, янв. 2011
Семейный бизнес в России. Информационно-аналитическая справка, янв. 2011Олег Муковозов
 
Репутация глобальный взгляд IPSOS 2013-rus
Репутация глобальный взгляд IPSOS 2013-rusРепутация глобальный взгляд IPSOS 2013-rus
Репутация глобальный взгляд IPSOS 2013-rusОлег Муковозов
 
Accreditation: Future developments and their impacts
Accreditation: Future developments and their impactsAccreditation: Future developments and their impacts
Accreditation: Future developments and their impactsmarlowa1234
 
отраслевой доклад 2014. тв в россии
отраслевой доклад 2014. тв в россииотраслевой доклад 2014. тв в россии
отраслевой доклад 2014. тв в россииОлег Муковозов
 
Средний класс в современной России. Институт социологии РАН, 2014
Средний класс в современной России. Институт социологии РАН, 2014Средний класс в современной России. Институт социологии РАН, 2014
Средний класс в современной России. Институт социологии РАН, 2014Олег Муковозов
 

Destacado (20)

Marta Leo Smau16
Marta Leo Smau16Marta Leo Smau16
Marta Leo Smau16
 
Детские порталы. Особенности поведения детской аудитории
Детские порталы. Особенности поведения детской аудиторииДетские порталы. Особенности поведения детской аудитории
Детские порталы. Особенности поведения детской аудитории
 
Drug addiction
Drug addiction   Drug addiction
Drug addiction
 
Key west 2010
Key west 2010Key west 2010
Key west 2010
 
LowCountryVolkswagen.com_AAA Cell Phones And Driving Research Update
LowCountryVolkswagen.com_AAA Cell Phones And Driving Research UpdateLowCountryVolkswagen.com_AAA Cell Phones And Driving Research Update
LowCountryVolkswagen.com_AAA Cell Phones And Driving Research Update
 
Ipad in education 2
Ipad in education 2Ipad in education 2
Ipad in education 2
 
Social Media
Social MediaSocial Media
Social Media
 
Mobile 101 Class 4: Mobile Measurement
Mobile 101 Class 4: Mobile MeasurementMobile 101 Class 4: Mobile Measurement
Mobile 101 Class 4: Mobile Measurement
 
Профессиональный стандарт специалиста по связям с общественностью
Профессиональный стандарт специалиста по связям с общественностьюПрофессиональный стандарт специалиста по связям с общественностью
Профессиональный стандарт специалиста по связям с общественностью
 
LowCountryVolkswagen.com_AAA Aggressive Driving Research Update
LowCountryVolkswagen.com_AAA Aggressive Driving Research UpdateLowCountryVolkswagen.com_AAA Aggressive Driving Research Update
LowCountryVolkswagen.com_AAA Aggressive Driving Research Update
 
The Media Kitchen - 2014 Digital Innovation Currents
The Media Kitchen - 2014 Digital Innovation CurrentsThe Media Kitchen - 2014 Digital Innovation Currents
The Media Kitchen - 2014 Digital Innovation Currents
 
E learning futures
E learning futuresE learning futures
E learning futures
 
Отраслевой доклад 2016. Полиграфия России
Отраслевой доклад 2016. Полиграфия РоссииОтраслевой доклад 2016. Полиграфия России
Отраслевой доклад 2016. Полиграфия России
 
Отраслевой доклад 2015. Печатные СМИ в России
Отраслевой доклад 2015. Печатные СМИ в РоссииОтраслевой доклад 2015. Печатные СМИ в России
Отраслевой доклад 2015. Печатные СМИ в России
 
Trĩ_Nguyễn Chính Hiếu_Y09A
Trĩ_Nguyễn Chính Hiếu_Y09ATrĩ_Nguyễn Chính Hiếu_Y09A
Trĩ_Nguyễn Chính Hiếu_Y09A
 
Семейный бизнес в России. Информационно-аналитическая справка, янв. 2011
Семейный бизнес в России. Информационно-аналитическая справка, янв. 2011Семейный бизнес в России. Информационно-аналитическая справка, янв. 2011
Семейный бизнес в России. Информационно-аналитическая справка, янв. 2011
 
Репутация глобальный взгляд IPSOS 2013-rus
Репутация глобальный взгляд IPSOS 2013-rusРепутация глобальный взгляд IPSOS 2013-rus
Репутация глобальный взгляд IPSOS 2013-rus
 
Accreditation: Future developments and their impacts
Accreditation: Future developments and their impactsAccreditation: Future developments and their impacts
Accreditation: Future developments and their impacts
 
отраслевой доклад 2014. тв в россии
отраслевой доклад 2014. тв в россииотраслевой доклад 2014. тв в россии
отраслевой доклад 2014. тв в россии
 
Средний класс в современной России. Институт социологии РАН, 2014
Средний класс в современной России. Институт социологии РАН, 2014Средний класс в современной России. Институт социологии РАН, 2014
Средний класс в современной России. Институт социологии РАН, 2014
 

Similar a Objective c beginner's guide

Similar a Objective c beginner's guide (20)

C
CC
C
 
iOS,From Development to Distribution
iOS,From Development to DistributioniOS,From Development to Distribution
iOS,From Development to Distribution
 
C# tutorial
C# tutorialC# tutorial
C# tutorial
 
Objective c intro (1)
Objective c intro (1)Objective c intro (1)
Objective c intro (1)
 
Switch case and looping
Switch case and loopingSwitch case and looping
Switch case and looping
 
Srgoc dotnet
Srgoc dotnetSrgoc dotnet
Srgoc dotnet
 
Complete C programming Language Course
Complete C programming Language CourseComplete C programming Language Course
Complete C programming Language Course
 
Srgoc dotnet_new
Srgoc dotnet_newSrgoc dotnet_new
Srgoc dotnet_new
 
c.ppt
c.pptc.ppt
c.ppt
 
Cocoa for Web Developers
Cocoa for Web DevelopersCocoa for Web Developers
Cocoa for Web Developers
 
C++ Core Guidelines
C++ Core GuidelinesC++ Core Guidelines
C++ Core Guidelines
 
Ppt of c vs c#
Ppt of c vs c#Ppt of c vs c#
Ppt of c vs c#
 
C tutorial
C tutorialC tutorial
C tutorial
 
1 CMPS 12M Introduction to Data Structures Lab La.docx
1 CMPS 12M Introduction to Data Structures Lab La.docx1 CMPS 12M Introduction to Data Structures Lab La.docx
1 CMPS 12M Introduction to Data Structures Lab La.docx
 
Presentation 5th
Presentation 5thPresentation 5th
Presentation 5th
 
1 c introduction
1 c introduction1 c introduction
1 c introduction
 
Introduction of c language
Introduction of c languageIntroduction of c language
Introduction of c language
 
Introduction Of C++
Introduction Of C++Introduction Of C++
Introduction Of C++
 
C++ idioms.pptx
C++ idioms.pptxC++ idioms.pptx
C++ idioms.pptx
 
Introduction-to-C-Part-1.pptx
Introduction-to-C-Part-1.pptxIntroduction-to-C-Part-1.pptx
Introduction-to-C-Part-1.pptx
 

Objective c beginner's guide

  • 1. 24/11/10 21:11Objective-C Beginner's Guide Página 1 de 34http://www.otierney.net/objective-c.html Translations: English | Chinese | Korean Outline Getting Started Downloading this tutorial Setting up the environment Preamble Making hello world Creating Classes @interface @implementation Piecing it together The Details... Multiple Parameters Constructors Access Privledges Class level access Exceptions Inheritance, Polymorphism, and other OOP features The id type Inheritance Dynamic types Categories Posing Protocols Memory Management Retain and Release Dealloc Autorelease Pool Foundation Framework Classes NSArray NSDictionary Pros and Cons More Information Getting Started 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. 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
  • 2. 24/11/10 21:11Objective-C Beginner's Guide Página 2 de 34http://www.otierney.net/objective-c.html 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 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. Making hello world hello.m #import <stdio.h> int main( int argc, const char *argv[] ) { printf( "hello worldn" ); return 0; } output hello world You use #import instead of #include in Objective-C The default file extention for Objective-C is .m
  • 3. 24/11/10 21:11Objective-C Beginner's Guide Página 3 de 34http://www.otierney.net/objective-c.html Creating classes @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 @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; }
  • 4. 24/11/10 21:11Objective-C Beginner's Guide Página 4 de 34http://www.otierney.net/objective-c.html -(int) numerator { return numerator; } @end @implementation ClassName starts the implementation @end ends it All the defined methods are implemented very simlar to how they are declared in the interface Piecing it together Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission main.m #import <stdio.h> #import "Fraction.h" int main( int argc, const char *argv[] ) { // create a new instance Fraction *frac = [[Fraction alloc] init]; // set the values [frac setNumerator: 1]; [frac setDenominator: 3]; // print it printf( "The fraction is: " ); [frac print]; printf( "n" ); // free memory [frac release]; return 0; } output The fraction is: 1/3 Fraction *frac = [[Fraction alloc] init]; There are several important things in this one line. The way methods in Objective-C are called is [object method], which is similar to object->method() in C++ Objective-C doesn't have value types, so there is nothing similar to C++'s: Fraction frac; frac.print();. You always deal with objects as pointers in Objective-C. What this line is really doing is two things: [Fraction alloc] is calling the alloc method on the Fraction class. This is similar to mallocing memory, because that is all that is done in this operation. [object init] is the constructor call, which initializes any variables in the object. This method is called on the instance returned from [Fraction alloc]. This operation is so common it's usually just done in one line as Object *var = [[Object alloc] init]; [frac setNumerator: 1] is quite simple. It's calling the setNumerator method on frac, and passing it the parameter 1. Like every c variant, there's a construct for freeing memory. This is done via release, which is inherited from NSObject. This method will be explainted in greater detail
  • 5. 24/11/10 21:11Objective-C Beginner's Guide Página 5 de 34http://www.otierney.net/objective-c.html later. The details... 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
  • 6. 24/11/10 21:11Objective-C Beginner's Guide Página 6 de 34http://www.otierney.net/objective-c.html The fraction is: 1/3 Fraction 2 is: 1/5 The method is actually called setNumerator:andDenominator: Additional parameters are added the same was as the 2nd, such that you'd have method:label1:label2:label3: and you'd call it with [obj method: param1 label1: param2 label2: param3 label3: param4] Labels are optional. It's possible to have a method named method:::. This is done by simply not specifing label names, but just a : to separate the parameters. This is however not advised. Constructors Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission Fraction.h ... -(Fraction*) initWithNumerator: (int) n denominator: (int) d; ... 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: " );
  • 7. 24/11/10 21:11Objective-C Beginner's Guide Página 7 de 34http://www.otierney.net/objective-c.html [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 @interface declaration is identical to a regular function @implementation shows a new keyword: super Similar to Java, Objective-C only has one parent class. Accessing it's super constructor is done through [super init] and this is required for proper inheritance. This returns an instance which you assign to another new keyword, self. Self is similar to this in Java and C++. if ( self ) is the same as if ( self != nil ) to make sure that the super constructor successfully returned a new object. nil is Objective-C's form of NULL from C/C++. This is gotten from including NSObject. After you've initialized the varialbes, you return yourself with return self; The deafult constructor is -(id) init; Constructors in Objective-C are technically just "init" methods, they aren't a special construct like they are in C++ and Java. Access Privledges The default access is @protected Java implements this with public/private/protected modifiers infront of methods and variables. Objective-C's approach is much more similar to C++'s for instance variables Access.h #import <Foundation/NSObject.h> @interface Access: NSObject { @public int publicVar; @private int privateVar; int privateVar2; @protected int protectedVar; } @end Access.m #import "Access.h" @implementation Access @end main.m
  • 8. 24/11/10 21:11Objective-C Beginner's Guide Página 8 de 34http://www.otierney.net/objective-c.html #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: %in", a->publicVar ); // doesn't compile //a->privateVar = 10; //printf( "private var: %in", 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. 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"
  • 9. 24/11/10 21:11Objective-C Beginner's Guide Página 9 de 34http://www.otierney.net/objective-c.html #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: %in", [ClassA initCount] ); ClassA *c3 = [[ClassA alloc] init]; // print count again printf( "ClassA count: %in", [ClassA initCount] ); [c1 release]; [c2 release]; [c3 release]; return 0; } output ClassA count: 2 ClassA count: 3 static int count = 0; This is how the class variable is declared. This is not the ideal place for such a variable. A nicer solution would have been like Java's implementation of static class variables. However this works +(int) initCount; This is the actual method that returns the count. Notice the subtle difference. Instead of using a - infront of the type, a + is used. The + denotes a class level function. Accessing the variable is no different than member variables, as seen by count++ in the constructor of ClassA. The +(void) initialize method is called when Objective-C starts your program, and it's called for every class. This is a good place to initialize class level variables like our count. Exceptions NOTE: Exception handling is only supported in Mac OS X 10.3 Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission CupWarningException.h #import <Foundation/NSException.h> @interface CupWarningException: NSException @end CupWarningException.m #import "CupWarningException.h" @implementation CupWarningException @end CupOverflowException.h #import <Foundation/NSException.h>
  • 10. 24/11/10 21:11Objective-C Beginner's Guide Página 10 de 34http://www.otierney.net/objective-c.html @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 ) {
  • 11. 24/11/10 21:11Objective-C Beginner's Guide Página 11 de 34http://www.otierney.net/objective-c.html // 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: %in", 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: %sn", [[e name] cString], [[e reason] cString] ); } // free memory [cup release]; [pool release]; }
  • 12. 24/11/10 21:11Objective-C Beginner's Guide Página 12 de 34http://www.otierney.net/objective-c.html 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 NSAutoreleasePool is a memory management class. Don't worry about what this does right now. Exceptions that are thrown don't have to extend NSException. You can just as easily use an id as well: @catch ( id e ) { ... } There is also a finally block, which behaves just like Java's. The contents of a finally block are guaranteed to be called. The string as show in Cup.m, @"CupOverflowException", is a constant NSString object. The @ sign is used often in Objective-C to denote extentions to the language. A C string is just like C and C++, "String constant", and is of type char *. Inheritance, Polymorphism, and other OOP features The id type Objective-C has a type called id, that acts in some ways like a void*, though it's meant strictly for objects. Objective-C differs from Java and C++ in that when you call a method on an object, it doesn't need to know the type. That method simply just has to exist. This is refered to as message pasing in Objective-C. 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; } -(Fraction*) initWithNumerator: (int) n denominator: (int) d; -(void) print; -(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>
  • 13. 24/11/10 21:11Objective-C Beginner's Guide Página 13 de 34http://www.otierney.net/objective-c.html @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 ) {
  • 14. 24/11/10 21:11Objective-C Beginner's Guide Página 14 de 34http://www.otierney.net/objective-c.html [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
  • 15. 24/11/10 21:11Objective-C Beginner's Guide Página 15 de 34http://www.otierney.net/objective-c.html The complex number is: 10.000000 + 15.000000i There are obvious benefits to this type of dynamic binding. You don't have to know the type of something to call a method on it. If the object responds to a message, it will invoke that method. Lots of nasty casting isn't involved in this either, such as in Java to call .intValue() on an integer object would involve casting first, then calling the method. Inheritance Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission Rectangle.h #import <Foundation/NSObject.h> @interface Rectangle: NSObject { int width; int height; } -(Rectangle*) initWithWidth: (int) w height: (int) h; -(void) setWidth: (int) w; -(void) setHeight: (int) h; -(void) setWidth: (int) w height: (int) h; -(int) width; -(int) height; -(void) print; @end 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 {
  • 16. 24/11/10 21:11Objective-C Beginner's Guide Página 16 de 34http://www.otierney.net/objective-c.html 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" );
  • 17. 24/11/10 21:11Objective-C Beginner's Guide Página 17 de 34http://www.otierney.net/objective-c.html 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 Inheritance in Objective-C is similar to Java. When you extend your super class (of which you can only have one parent) you can override the methods of your super class by simply putting the new implementations in the child classes implementation. No fooling with virtual tables like C++. One thing left out here that is worth nothing is what would happen if you attempted to call the constructor for rectangle like: Square *sq = [[Square alloc] initWithWidth: 10 height: 15]. The answer is it will throw a compile error. Since the return type of the rectangle constructor is Rectangle*, not Square* this would not work. In such a case if you want this to occur, that's what the id variable is good for. Just change the Rectangle* return type to id if you wish to use your parent's constructors in a subclass. Dynamic types There are several methods for working with dynamic types in Objective-C -(BOOL) isKindOfClass: classObj is object a descendent or member of classObj -(BOOL) isMemberOfClass: classObj is object a member of classObj -(BOOL) respondsToSelector: selector does the object have a method named specifiec by the selector +(BOOL) instancesRespondToSelector: selector does an object created by this class have the ability to respond to the specified selector -(id) performSelector: selector invoke the specified selector on the object Every object inherited from NSObject has a class method that returns a class object. This is very similar to Java's getClass() method. This class object is used in the methods above. Selectors are used to represent a message in Objective-C. The syntax for creating a selector is shown in the next example Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission main.m #import "Square.h" #import "Rectangle.h"
  • 18. 24/11/10 21:11Objective-C Beginner's Guide Página 18 de 34http://www.otierney.net/objective-c.html #import <stdio.h> int main( int argc, const char *argv[] ) { Rectangle *rec = [[Rectangle alloc] initWithWidth: 10 height: 20]; Square *sq = [[Square alloc] initWithSize: 15]; // isMemberOfClass // true if ( [sq isMemberOfClass: [Square class]] == YES ) { printf( "square is a member of square classn" ); } // false if ( [sq isMemberOfClass: [Rectangle class]] == YES ) { printf( "square is a member of rectangle classn" ); } // false if ( [sq isMemberOfClass: [NSObject class]] == YES ) { printf( "square is a member of object classn" ); } // isKindOfClass // true if ( [sq isKindOfClass: [Square class]] == YES ) { printf( "square is a kind of square classn" ); } // true if ( [sq isKindOfClass: [Rectangle class]] == YES ) { printf( "square is a kind of rectangle classn" ); } // true if ( [sq isKindOfClass: [NSObject class]] == YES ) { printf( "square is a kind of object classn" ); } // respondsToSelector // true if ( [sq respondsToSelector: @selector( setSize: )] == YES ) { printf( "square responds to setSize: methodn" ); } // false if ( [sq respondsToSelector: @selector( nonExistant )] == YES ) { printf( "square responds to nonExistant methodn" ); } // true if ( [Square respondsToSelector: @selector( alloc )] == YES ) { printf( "square class responds to alloc methodn" ); } // instancesRespondToSelector // false if ( [Rectangle instancesRespondToSelector: @selector( setSize: )] == YES ) { printf( "rectangle instance responds to setSize: methodn" ); } // true if ( [Square instancesRespondToSelector: @selector( setSize: )] == YES ) { printf( "square instance responds to setSize: methodn" ); }
  • 19. 24/11/10 21:11Objective-C Beginner's Guide Página 19 de 34http://www.otierney.net/objective-c.html // free memory [rec release]; [sq release]; return 0; } 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 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]
  • 20. 24/11/10 21:11Objective-C Beginner's Guide Página 20 de 34http://www.otierney.net/objective-c.html 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 1/3 * 2/5 = 2/15 The magic here is the two @implementation and @interface lines: @interface Fraction (Math) and @implementation Fraction (Math). There can only be one category with the same name. Additional cateogies may be added on with different but unqiue names. Categories can't add instance variables. Categories are useful for creating private methods. Since Objective-C has no notion of private/protected/public methods like java does, one has to create categories that hide such functionality. The way this is done is by moving the private methods from your class's header (.h) file to the implementation file (.m). The following is a very brief example of what I mean. MyClass.h #import <Foundation/NSObject.h> @interface MyClass: NSObject -(void) publicMethod; @end MyClass.m #import "MyClass.h" #import <stdio.h> @implementation MyClass -(void) publicMethod { printf( "public methodn" ); }
  • 21. 24/11/10 21:11Objective-C Beginner's Guide Página 21 de 34http://www.otierney.net/objective-c.html @end // private methods @interface MyClass (Private) -(void) privateMethod; @end @implementation MyClass (Private) -(void) privateMethod { printf( "private methodn" ); } @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 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 ); }
  • 22. 24/11/10 21:11Objective-C Beginner's Guide Página 22 de 34http://www.otierney.net/objective-c.html @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) The output from this program would print the first fraction s 3/10. The second would output (3/10), which is implemented by FractionB. The method poseAsClass is part of NSObject. This allows a subclass to pose as a superclass. Protocols A Protocol in Objective-C is identical in functionality to an interface in Java, or a purely virtual class in C++. Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission Printing.h @protocol Printing -(void) print; @end Fraction.h #import <Foundation/NSObject.h> #import "Printing.h" @interface Fraction: NSObject <Printing, NSCopying> { int numerator;
  • 23. 24/11/10 21:11Objective-C Beginner's Guide Página 23 de 34http://www.otierney.net/objective-c.html 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 denominator: denominator]; } @end Complex.h #import <Foundation/NSObject.h> #import "Printing.h" @interface Complex: NSObject <Printing> { double real; double imaginary; }
  • 24. 24/11/10 21:11Objective-C Beginner's Guide Página 24 de 34http://www.otierney.net/objective-c.html -(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: " );
  • 25. 24/11/10 21:11Objective-C Beginner's Guide Página 25 de 34http://www.otierney.net/objective-c.html [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 NSCopyingn" ); } // false if ( [comp conformsToProtocol: @protocol( NSCopying )] == YES ) { printf( "Complex conforms to NSCopyingn" ); } // 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 The protocol specification is quite simple. it is basically @protocol ProtocolName (methods you must implement) @end. To conform to a protocol, you put the protocols you're conforming to in <>'s, and comma separate them. Example: @interface SomeClass <Protocol1, Protocol2, Protocol3> The methods that the protocol requires to be implemented are not required to be in the list of methods for the header file. As you can see, Complex.h doesn't have a definition for -(void) print, but it still implements it since it conforms to the protocol. One unique aspect of Objective-C's interface system is how you specify types. Rather than specifying it like Java or C++ as: Printing *someVar = ( Printing * ) frac; for example, you use the id type with a restricted protocol: id <Printing> var = frac; This allows you to dynamically specify a type that requires multiple protocols, all with one variable. Such as: id <Printing, NSCopying> var = frac; Much like using @selector for testing an object's inheritance, you can use @protocol to test for conformance of interfaces. [object conformsToProtocol: @protocol( SomeProtocol )] returns a BOOL if the object conforms to that protocol. This works the same for classes as well: [SomeClass conformsToProtocol: @protocol( SomeProtocol )]. Memory Management
  • 26. 24/11/10 21:11Objective-C Beginner's Guide Página 26 de 34http://www.otierney.net/objective-c.html 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. Note: everything up until this point has been properly memory managed, incase you're wondering. 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 fractionn" ); [super dealloc]; } ... Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission main.m #import "Fraction.h" #import <stdio.h> int main( int argc, const char *argv[] ) { Fraction *frac1 = [[Fraction alloc] init]; Fraction *frac2 = [[Fraction alloc] init]; // print current counts printf( "Fraction 1 retain count: %in", [frac1 retainCount] ); printf( "Fraction 2 retain count: %in", [frac2 retainCount] ); // increment them [frac1 retain]; // 2 [frac1 retain]; // 3 [frac2 retain]; // 2 // print current counts printf( "Fraction 1 retain count: %in", [frac1 retainCount] ); printf( "Fraction 2 retain count: %in", [frac2 retainCount] ); // decrement [frac1 release]; // 2 [frac2 release]; // 1 // print current counts printf( "Fraction 1 retain count: %in", [frac1 retainCount] ); printf( "Fraction 2 retain count: %in", [frac2 retainCount] ); // release them until they dealloc themselves [frac1 release]; // 1 [frac1 release]; // 0 [frac2 release]; // 0
  • 27. 24/11/10 21:11Objective-C Beginner's Guide Página 27 de 34http://www.otierney.net/objective-c.html } 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 The retain calls increment the counter. The release calls decrement it. One can get the count as an int by calling [obj retainCount]. Once the retainCount reaches 0, both objects dealloc themselves and you can see this when both print out "Deallocing fraction." Dealloc When your object contains other objects, you must free them whenever you yourself dealloc. One of the nice advantages to Objective-C is you can pass messages to nil, so there isn't a lot of error checking to release an object. Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission AddressCard.h #import <Foundation/NSObject.h> #import <Foundation/NSString.h> @interface AddressCard: NSObject { NSString *first; NSString *last; NSString *email; } -(AddressCard*) initWithFirst: (NSString*) f last: (NSString*) l email: (NSString*) e; -(NSString*) first; -(NSString*) last; -(NSString*) email; -(void) setFirst: (NSString*) f; -(void) setLast: (NSString*) l; -(void) setEmail: (NSString*) e; -(void) setFirst: (NSString*) f last: (NSString*) l email: (NSString*) e; -(void) setFirst: (NSString*) f last: (NSString*) l; -(void) print; @end AddressCard.m #import "AddressCard.h" #import <stdio.h> @implementation AddressCard -(AddressCard*) initWithFirst: (NSString*) f last: (NSString*) l email: (NSString*) e { self = [super init];
  • 28. 24/11/10 21:11Objective-C Beginner's Guide Página 28 de 34http://www.otierney.net/objective-c.html 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
  • 29. 24/11/10 21:11Objective-C Beginner's Guide Página 29 de 34http://www.otierney.net/objective-c.html #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: "tom@jones.com"]; AddressCard *tom = [[AddressCard alloc] initWithFirst: first last: last email: 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: %in", [[tom first] retainCount] ); [tom print]; printf( "n" ); // free memory [tom release]; return 0; } output Retain count: 1 Tom Jones <tom@jones.com> This example shows not only how to make a dealloc method, as shown in AddressCard.m, but one way to do member variables. The order of the 3 operations in each set method is very important. Lets say you'return passing a parameter of yourself to one of your methods (a bit of an odd example, but this can happen). If you release first, THEN retain you will destruct yourself! That's why you should always 1) retain 2) release 3) set the value. Normally one wouldn't initialize variables with C strings because they don't support unicode. The next example, with NSAutoreleasePool shows the proper way to do strings and initializing. This is just one way of handling member variable memory management. One way to handle this is to create copies inside your set methods. Autorelease Pool When you want to start doing more programming using NSString and other Foundation framework classes you need a more flexible system. This system is using Autorelease pools. When developing Mac Cocoa applications, the auto release pool is setup automatically for you. Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission main.m #import <Foundation/NSString.h> #import <Foundation/NSAutoreleasePool.h> #import <stdio.h> int main( int argc, const char *argv[] ) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  • 30. 24/11/10 21:11Objective-C Beginner's Guide Página 30 de 34http://www.otierney.net/objective-c.html NSString *str1 = @"constant string"; NSString *str2 = [NSString stringWithString: @"string managed by the pool"]; NSString *str3 = [[NSString alloc] initWithString: @"self managed string"]; // print the strings printf( "%s retain count: %xn", [str1 cString], [str1 retainCount] ); printf( "%s retain count: %xn", [str2 cString], [str2 retainCount] ); printf( "%s retain count: %xn", [str3 cString], [str3 retainCount] ); // free memory [str3 release]; // free pool [pool release]; return 0; } 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[] ) {
  • 31. 24/11/10 21:11Objective-C Beginner's Guide Página 31 de 34http://www.otierney.net/objective-c.html 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 In this example, the method is a class level method. After the object is created, autorelease is called on it. Inside the body of the main method, I never call release on the object. The reason this works is because: for every retain, one release or autorelease must be called. The object's retain count starts out as 1, and I called autorelease on it once. This means 1 - 1 = 0. Once the autorelease pool is released, it counts the autorelease calls on all objects and decrements them with [obj release] with the same number of times autorelease was called per object. As the comment says, uncommenting that line causes a segment fault. Since autorelease was already called on the object, calling release on it, and then releasing the autorelease pool would attempt to call dealloc on an object that is nil, which is not valid. The end math is 1 (creation) - 1 (release) - 1 (autorelease) = -1. Auto release pools can be dynamically created for large amounts of temporary objects. All one must do is create a pool, perform any large chunk of code that creates lots of temporary objects, then release the pool. As you may wonder, it this means it is possible to have more than one auto release pool at a time. Foundation framework classes The Foundation framework is similar to C++'s Standard Template Library. Although since Objective-C has real dynamic types, the horrible cludge that is C++'s templating is not necessary. This framework contains collections, networking, threading, and much more. NSArray Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission main.m #import <Foundation/NSArray.h> #import <Foundation/NSString.h> #import <Foundation/NSAutoreleasePool.h>
  • 32. 24/11/10 21:11Objective-C Beginner's Guide Página 32 de 34http://www.otierney.net/objective-c.html #import <Foundation/NSEnumerator.h> #import <stdio.h> void print( NSArray *array ) { NSEnumerator *enumerator = [array objectEnumerator]; id obj; while ( obj = [enumerator nextObject] ) { printf( "%sn", [[obj description] cString] ); } } int main( int argc, const char *argv[] ) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSArray *arr = [[NSArray alloc] initWithObjects: @"Me", @"Myself", @"I", nil]; NSMutableArray *mutable = [[NSMutableArray alloc] init]; // enumerate over items printf( "----static arrayn" ); print( arr ); // add stuff [mutable addObject: @"One"]; [mutable addObject: @"Two"]; [mutable addObjectsFromArray: arr]; [mutable addObject: @"Three"]; // print em printf( "----mutable arrayn" ); print( mutable ); // sort then print printf( "----sorted mutable arrayn" ); [mutable sortUsingSelector: @selector( caseInsensitiveCompare: )]; print( mutable ); // free memory [arr release]; [mutable release]; [pool release]; return 0; } output ----static array Me Myself I ----mutable array One Two Me Myself I Three ----sorted mutable array I Me Myself One Three Two There are two kinds of arrays (and of usually most data oriented Foundation classes)
  • 33. 24/11/10 21:11Objective-C Beginner's Guide Página 33 de 34http://www.otierney.net/objective-c.html NSArray and NSMutableArray. As the name suggests, Mutable is changable, NSArray then is not. This means you can make an NSArray but once you have you can't change the length. You initialize an array via the constructor using Obj, Obj, Obj, ..., nil. The nil is an ending delimiter. The sorting shows how to sort an object using a selector. The selector tells the array to sort using NSString's case insensitive compare. If your object has several sort methods, you can choose anyone you want using this selector. In the print method, I used the method description. This is similar to Java's toString. It returns an NSString representation of an object. NSEnumerator is similar to Java's enumerator system. The reason why while ( obj = [array objectEnumerator] ) works is because objectEnumerator returns nil on the last object. Since in C nil is usually 0, this is the same as false. ( ( obj = [array objectEnumerator] ) != nil ) might be preferable NSDictionary Based on an example in "Programming in Objective-C," Copyright © 2004 by Sams Publishing. Used with permission main.m #import <Foundation/NSString.h> #import <Foundation/NSAutoreleasePool.h> #import <Foundation/NSDictionary.h> #import <Foundation/NSEnumerator.h> #import <Foundation/Foundation.h< #import <stdio.h> void print( NSDictionary *map ) { NSEnumerator *enumerator = [map keyEnumerator]; id key; while ( key = [enumerator nextObject] ) { printf( "%s => %sn", [[key description] cString], [[[map objectForKey: key] description] cString] ); } } int main( int argc, const char *argv[] ) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys: @"one", [NSNumber numberWithInt: 1], @"two", [NSNumber numberWithInt: 2], @"three", [NSNumber numberWithInt: 3], nil]; NSMutableDictionary *mutable = [[NSMutableDictionary alloc] init]; // print dictionary printf( "----static dictionaryn" ); print( dictionary ); // add objects [mutable setObject: @"Tom" forKey: @"tom@jones.com"]; [mutable setObject: @"Bob" forKey: @"bob@dole.com" ]; // print mutable dictionary printf( "----mutable dictionaryn" ); print( mutable ); // free memory [dictionary release]; [mutable release];
  • 34. 24/11/10 21:11Objective-C Beginner's Guide Página 34 de 34http://www.otierney.net/objective-c.html [pool release]; return 0; } output ----static dictionary 1 => one 2 => two 3 => three ----mutable dictionary bob@dole.com => Bob tom@jones.com => Tom Pros and Cons Pros Cateogies Posing Dynamic typing Pointer counting Flexible message passing Not an overly complex extention to C Can interface with C++ via Objective-C++ 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 Programming in Objective-C 2.0 (2nd Edition) Programming in Objective-C Learning Cocoa with Objective-C Cocoa Programming for Mac OS X Object-Oriented Programming and the Objective-C Language GNUstep mini tutorials Last modified: April 13, 2004.