CS193P - Lecture 3
iPhone Application Development
Custom Classes Object Lifecycle Autorelease Properties
Announcements
Assignments 1A and 1B due Thursday 4/9 at 11:59 PM
Enrolled Stanford students can email cs193p@cs.stanford.edu with any questions ! Submit early! Instructions on the website...
!
!
Delete the build directory manually, Xcode wont do it
Announcements
Assignments 2A and 2B due Tuesday 4/14 at 11:59 PM
!
2A: Continuation of Foundation tool
Add custom class ! Basic memory management
!
2B: Beginning of first iPhone application
Topics to be covered on Monday 4/13 ! Assignment contains extensive walkthrough
!
Announcements
Troys office hours: Mondays 12-2, Gates B26A Pauls office hours: Tuesdays 12-2, Gates 463 This weeks optional Friday session (4/10)
200-205, 3:15 - 4:05 PM ! Debugging crash course, not to be missed!
!
Class newsgroup (Stanford-only) at su.class.cs193p
!
No gopher site yet...
Todays Topics
Questions from Assignment 1A or 1B? Creating Custom Classes Object Lifecycle Autorelease Objective-C Properties
Custom Classes
Design Phase
Create a class
!
Person NSObject (in this case) Name, age, whether they can vote Cast a ballot
Determine the superclass
!
What properties should it have?
!
What actions can it perform?
!
Review: Methods, Selectors, Messages
Method
!
Behavior associated with an object
- (NSString *)name { // Implementation } - (void)setName:(NSString *)name { // Implementation }
Review: Methods, Selectors, Messages
Selector
Name for referring to a method ! Includes colons to indicate arguments ! Doesnt actually include arguments or indicate types
!
SEL mySelector = @selector(name); SEL anotherSelector = @selector(setName:); SEL lastSelector = @selector(doStuff:withThing:andThing:);
Review: Methods, Selectors, Messages
Message
The act of performing a selector on an object ! With arguments, if necessary
!
NSString *name = [myPerson name]; [myPerson setName:@New Name];
Defining a class
A public header and a private implementation
Header File
Implementation File
Defining a class
A public header and a private implementation
Header File
Implementation File
Class interface declared in header file
#import <Foundation/Foundation.h> @interface Person : NSObject { // instance variables NSString *name; int age; } // method declarations - (NSString *)name; - (void)setName:(NSString *)value; - (int)age; - (void)setAge:(int)age; - (BOOL)canLegallyVote; - (void)castBallot; @end
Defining a class
A public header and a private implementation
Header File
Implementation File
Implementing custom class
Implement setter/getter methods Implement action methods
Class Implementation
#import "Person.h" @implementation Person - (int)age { return age; } - (void)setAge:(int)value { age = value; } //... and other methods @end
Calling your own methods
#import "Person.h" @implementation Person - (BOOL)canLegallyVote { return ([self age] >= 18); } - (void)castBallot { if ([self canLegallyVote]) { ! ! // do voting stuff } else { ! ! NSLog (@Im not allowed to vote!); } } @end
Superclass methods
As we just saw, objects have an implicit variable named self
!
Like this in Java and C++
- (void)doSomething { // Call superclass implementation first [super doSomething]; // Then do our custom behavior int foo = bar; // ... }
Can also invoke superclass methods using super
Object Lifecycle
Object Lifecycle
Creating objects Memory management Destroying objects
Object Creation
Two step process
allocate memory to store the object ! initialize object state
!
+ alloc
!
Class method that knows how much memory is needed Instance method to set initial values, perform other setup
- init
!
Create = Allocate + Initialize
Person *person = nil; person = [[Person alloc] init];
Implementing your own -init method
#import "Person.h" @implementation Person - (id)init { // allow superclass to initialize its state first if (self = [super init]) { age = 0; name = @Bob; // do other initialization...
} @end
return self;
Multiple init methods
Classes may define multiple init methods
- (id)init; - (id)initWithName:(NSString *)name; - (id)initWithName:(NSString *)name age:(int)age;
Less specific ones typically call more specific with default values
- (id)init { return [self initWithName:@No Name]; } - (id)initWithName:(NSString *)name { return [self initWithName:name age:0]; }
Finishing Up With an Object
Person *person = nil; person = [[Person alloc] init]; [person setName:@Alan Cannistraro]; [person setAge:29]; [person setWishfulThinking:YES]; [person castBallot]; // What do we do with person when were done?
Memory Management
Allocation C Objective-C
malloc alloc
Destruction
free dealloc
Calls must be balanced
!
Otherwise your program may leak or crash One exception, well see in a bit...
However, youll never call -dealloc directly
!
Reference Counting
Every object has a retain count
Defined on NSObject ! As long as retain count is > 0, object is alive and valid
!
+alloc and -copy create objects with retain count == 1 -retain increments retain count -release decrements retain count
When retain count reaches 0, object is destroyed
-dealloc method invoked automatically
!
One-way street, once youre in -dealloc theres no turning back
Balanced Calls
Person *person = nil; person = [[Person alloc] init]; [person setName:@Alan Cannistraro]; [person setAge:29]; [person setWishfulThinking:YES]; [person castBallot]; // When were done with person, release it [person release]; // person will be destroyed here
Reference counting in action
Person *person = [[Person alloc] init];
Retain count begins at 1 with +alloc
[person retain];
Retain count increases to 2 with -retain
[person release];
Retain count decreases to 1 with -release
[person release];
Retain count decreases to 0, -dealloc automatically called
Messaging deallocated objects
Person *person = [[Person alloc] init]; // ... [person release]; // Object is deallocated
[person doSomething]; // Crash!
Messaging deallocated objects
Person *person = [[Person alloc] init]; // ... [person release]; // Object is deallocated person = nil; [person doSomething]; // No effect
Implementing a -dealloc method
#import "Person.h" @implementation Person - (void)dealloc { // Do any cleanup thats necessary // ... // when were done, call super to clean us up [super dealloc];
} @end
Object Lifecycle Recap
Objects begin with a retain count of 1 Increase and decrease with -retain and -release When retain count reaches 0, object deallocated automatically You never call dealloc explicitly in your code
Exception is calling -[super dealloc] ! You only deal with alloc, copy, retain, release
!
Object Ownership
#import <Foundation/Foundation.h> @interface Person : NSObject { // instance variables NSString *name; // Person class owns the name int age; } // method declarations - (NSString *)name; - (void)setName:(NSString *)value; - (int)age; - (void)setAge:(int)age; - (BOOL)canLegallyVote; - (void)castBallot; @end
Object Ownership
#import "Person.h" @implementation Person - (NSString *)name { return name; } - (void)setName:(NSString *)newName { if (name != newName) { [name release]; name = [newName retain]; // names retain count has been bumped up by 1 }
@end
Object Ownership
#import "Person.h" @implementation Person - (NSString *)name { return name; } - (void)setName:(NSString *)newName { if (name != newName) { [name release]; name = [newName copy]; // name has retain count of 1, we own it }
@end
Releasing Instance Variables
#import "Person.h" @implementation Person - (void)dealloc { // Do any cleanup thats necessary [name release]; // when were done, call super to clean us up [super dealloc];
} @end
Autorelease
Returning a newly created object
- (NSString *)fullName { NSString *result; result = [[NSString alloc] initWithFormat:@%@ %@, ! ! ! ! ! ! ! ! ! ! firstName, lastName];
return result; }
Wrong: result is leaked!
Returning a newly created object
- (NSString *)fullName { NSString *result; result = [[NSString alloc] initWithFormat:@%@ %@, ! ! ! ! ! ! ! ! ! ! firstName, lastName]; [result release]; return result; }
Wrong: result is released too early! Method returns bogus value
Returning a newly created object
- (NSString *)fullName { NSString *result; result = [[NSString alloc] initWithFormat:@%@ %@, ! ! ! ! ! ! ! ! ! ! firstName, lastName]; [result autorelease]; return result; }
Just right: result is released, but not right away Caller gets valid object and could retain if needed
Autoreleasing Objects
Calling -autorelease flags an object to be sent release at some
point in the future Lets you fulfill your retain/release obligations while allowing an object some additional time to live Makes it much more convenient to manage memory Very useful in methods which return a newly created object
Method Names & Autorelease
Methods whose names includes alloc or copy
return a retained object that the caller needs to release
NSMutableString *string = [[NSMutableString alloc] init]; // We are responsible for calling -release or -autorelease [string autorelease];
All other methods return autoreleased objects
NSMutableString *string = [NSMutableString string]; // The method name doesnt indicate that we need to release it // So dont- were cool!
This is a convention- follow it in methods you define!
How does -autorelease work?
Magic!
(Just kidding...)
How does -autorelease work?
Object is added to current autorelease pool Autorelease pools track objects scheduled to be released
!
When the pool itself is released, it sends -release to all its objects
UIKit automatically wraps a pool around every event dispatch
Autorelease Pools (in pictures)
Pool
Pool created
c un La
p ap h pp A
iti in
zed ali d oa L
ai m
nib n Wa
for it
t en ev
dle an H
t en ev
xit E
pp a
Autorelease Pools (in pictures)
Pool
Objects autoreleased here go into pool
Pool created
c un La
p ap h pp A
iti in
zed ali d oa L
ai m
nib n Wa
for it
t en ev
dle an H
t en ev
xit E
pp a
Autorelease Pools (in pictures)
Pool
Objects autoreleased here go into pool
[object autorelease];
Pool created
c un La
p ap h pp A
iti in
zed ali d oa L
ai m
nib n Wa
for it
t en ev
dle an H
t en ev
xit E
pp a
Autorelease Pools (in pictures)
Pool
Objects autoreleased here go into pool
Pool created
c un La
p ap h pp A
iti in
zed ali d oa L
ai m
nib n Wa
for it
t en ev
dle an H
t en ev
xit E
pp a
Autorelease Pools (in pictures)
Pool
Objects autoreleased here go into pool
Pool created
c un La
p ap h pp A
iti in
zed ali d oa L
ai m
nib n Wa
for it
t en ev
dle an H
t en ev
xit E
pp a
Autorelease Pools (in pictures)
Pool
Pool released
Objects autoreleased here go into pool
Pool created
c un La
p ap h pp A
iti in
zed ali d oa L
ai m
nib n Wa
for it
t en ev
dle an H
t en ev
xit E
pp a
Autorelease Pools (in pictures)
[object release];
Pool
[object release];
Objects autoreleased here go into pool
[object release]; Pool released
Pool created
c un La
p ap h pp A
iti in
zed ali d oa L
ai m
nib n Wa
for it
t en ev
dle an H
t en ev
xit E
pp a
Autorelease Pools (in pictures)
Pool
Pool released
Objects autoreleased here go into pool
Pool created
c un La
p ap h pp A
iti in
zed ali d oa L
ai m
nib n Wa
for it
t en ev
dle an H
t en ev
xit E
pp a
Hanging Onto an Autoreleased Object
Many methods return autoreleased objects
Remember the naming conventions... ! Theyre hanging out in the pool and will get released later
!
If you need to hold onto those objects you need to retain them
!
Bumps up the retain count before the release happens
name = [NSMutableString string]; // We want to name to remain valid! [name retain]; // ... // Eventually, well release it (maybe in our -dealloc?) [name release];
Side Note: Garbage Collection
Autorelease is not garbage collection Objective-C on iPhone OS does not have garbage collection
Objective-C Properties
Properties
Provide access to object attributes Shortcut to implementing getter/setter methods Also allow you to specify:
read-only versus read-write access ! memory management policy
!
Defining Properties
#import <Foundation/Foundation.h> @interface Person : NSObject { // instance variables NSString *name; int age; } // method declarations - (NSString *) name; - (void)setName:(NSString *)value; - (int) age; - (void)setAge:(int)age; - (BOOL) canLegallyVote; - (void)castBallot; @end
Defining Properties
#import <Foundation/Foundation.h> @interface Person : NSObject { // instance variables NSString *name; int age; } // method declarations - (NSString *) name; - (void)setName:(NSString *)value; - (int) age; - (void)setAge:(int)age; - (BOOL) canLegallyVote; - (void)castBallot; @end
Defining Properties
#import <Foundation/Foundation.h> @interface Person : NSObject { // instance variables NSString *name; int age; } // method declarations - (NSString *) name; - (void)setName:(NSString *)value; - (int) age; - (void)setAge:(int)age; - (BOOL) canLegallyVote; - (void)castBallot; @end
Defining Properties
#import <Foundation/Foundation.h> @interface Person : NSObject { // instance variables NSString *name; int age; } // property declarations @property int age ; @property (copy) NSString * name; @property (readonly) BOOL canLegallyVote ;
- (void)castBallot; @end
Defining Properties
#import <Foundation/Foundation.h> @interface Person : NSObject { // instance variables NSString *name; int age; } // property declarations @property int age; @property (copy) NSString *name; @property (readonly) BOOL canLegallyVote;
- (void)castBallot; @end
Synthesizing Properties
@implementation Person - (int)age { return age; } - (void)setAge:(int)value { age = value; } - (NSString *)name { return name; } - (void)setName:(NSString *)value { if (value != name) { [value release]; name = [value copy]; } } - (void)canLegallyVote { ...
Synthesizing Properties
@implementation Person - (int)age { return age; } - (void)setAge:(int)value { age = value; } - (NSString *)name { return name; } - (void)setName:(NSString *)value { if (value != name) { [value release]; name = [value copy]; } } - (void)canLegallyVote { ...
Synthesizing Properties
@implementation Person - (int)age { age return age; } - (void)setAge:(int)value { age = value; } - (NSString *)name { name return name; } - (void)setName:(NSString *)value { if (value != name) { [value release]; name = [value copy]; } } - (void)canLegallyVote { ...
Synthesizing Properties
@implementation Person @synthesize age; @synthesize name; - (BOOL)canLegallyVote { return (age > 17); } @end
Property Attributes
Read-only versus read-write
! @property int age; // read-write by default ! @property (readonly) BOOL canLegallyVote;
Memory management policies (only for object properties)
@property (assign) NSString *name; // pointer assignment ! @property (retain) NSString *name; // retain called ! @property (copy) NSString *name; // copy called
Property Names vs. Instance Variables
Property name can be different than instance variable
@interface Person : NSObject { ! ! ! int numberOfYearsOld; !} ! @property int age; ! @end ! ! ! @implementation Person @synthesize age = numberOfYearsOld; @end
Properties
Mix and match synthesized and implemented properties
@implementation Person @synthesize age; @synthesize name; - (void)setAge:(int)value { age = value; // now do something with the new age value... } @end
Setter method explicitly implemented Getter method still synthesized
Properties In Practice
Newer APIs use @property Older APIs use getter/setter methods Properties used heavily throughout UIKit APIs
!
Not so much with Foundation APIs Properties mean writing less code, but magic can sometimes be non-obvious
You can use either approach
!
Dot Syntax and self
When used in custom methods, be careful with dot syntax for
properties defined in your class References to properties and ivars behave very differently
@interface Person : NSObject { NSString *name; } @property (copy) NSString *name; @end @implementation Person - (void)doSomething { ! ! name = @Fred; ! ! self.name = @Fred; }
// accesses ivar directly! // calls accessor method
Common Pitfall with Dot Syntax
What will happen when this code executes?
@implementation Person - (void)setAge:(int)newAge { ! ! self.age = newAge; } @end
This is equivalent to:
@implementation Person - (void)setAge:(int)newAge { ! ! [self setAge:newAge]; // Infinite loop! } @end
Further Reading
Objective-C 2.0 Programming Language
Defining a Class ! Declared Properties
!
Memory Management Programming Guide for Cocoa
Questions?