Most of the app is merely connective tissue: cartilage and sinews. Xcode writes this part for you and we describe it in black. The part that you have to write is in red.
main
function is always the first function to be executed.
It calls the
UIApplicationMain
function.UIApplicationMain
function
creates two objects.
The first object is the application object,
always of class
UIApplication
.
The second object is the application delegate object,
of the class whose name is
the class prefix you specified
plus
"AppDelegate"
.
This class must be a
subclass
of class
NSObject <UIApplicationDelegate>
.UIView
.
You have to write this subclass yourself.
The second instance variable is the window object,
always of class
UIWindow
.
SubclassOfUIView *view; UIWindow *_window;
application:didFinishLaunchingWithOptions:
applicationDidFinishLaunching:
applicationWillTerminate:
.
(If
dealloc
is also sent to the application delegate,
it will come after
applicationWillTerminate:
.
dealloc
should
end by calling the
dealloc
method of its
super
object.)
initWithFrame:
.
This method should begin by calling the
initWithFrame:
of the
super
object inside the view.
It should then set the
backgroundColor
of the view.
drawRect:
method of the view should
draw the view.
Do not be unnerved because we never see who calls
drawRect:
.application:didFinishLaunchingWithOptions:
We have a memory manager, not a garbage collector.
Every object contains a instance variable named
retainCount
,
which is an
NSUInteger
(a non-negative whole number).
The object is guaranteed to stay alive as long as its
retainCount
is greater than zero.
When the object’s
retainCount
drops to zero,
the
memory
manager
can destroy the object.
The
retainCount
can never be negative.
The following statement prints
the object’s
retainCount
.
We need the
%u
format
because the
retainCount
is an
NSUInteger
,
not an
int
.
NSLog(@"myObject.retainCount == %u", myObject.retainCount);
When an object’s
retainCount
goes down to zero,
the object’s
dealloc
method is called
just before the object is destroyed by the memory manager.
Do not call
dealloc
yourself.
It will be called automatically.
The methods
alloc
,
new
,
and
copy
create a new object whose
retainCount
is initialized to 1.
If you call any of these methods,
you must eventually
release
(or later,
autorelease
)
the object.
If you don’t,
the object’s
retainCount
will never go back down to zero and
the object will never be destroyed by the memory manager.
The object will persist until the app is terminated
and will clog up the iPhone.
Often we use an object
(object4
in the following example)
that was created in another part of the app
and that will be destroyed in another
part of the app.
To ensure that the object is not destroyed while we are using it,
call
retain
just before you begin to use the object.
It adds 1 to the object’s
retainCount
.
When you are no longer interested in keeping the object alive,
call
release
.
It will subtract 1 from the object’s
retainCount
.
The
release
does not guarantee an immediate death for the object, however.
There may be many other parts of the app that have
retain
ed
object4
because
they
are interested in keeping
object4
alive.
//Assume object4 already exists. It was created somewhere else and will be //destroyed somewhere else. - (void) myMethod1 { //myObject1, myObject2, myObject3 will be used only within myMethod1. MyClass *myObject1 = [[MyClass alloc] init]; MyClass *myObject2 = [MyClass new]; MyClass *myObject3 = [myObject1 copy]; [object4 retain]; //use myObject1, myObject2, myObject3, object4; [object4 release]; //released because it was retained [myObject3 release]; //released because it was created with copy [myObject2 release]; //released because it was created with new [myObject1 release]; //released because it was created with alloc }
If you have not called any of these four methods,
you must not
release
(or
autorelease
)
the object.
- (void) myMethod2 { NSString *myObject1 = @"hello"; NSString *myObject2 = [NSString stringWithFormat: @"%d/%d/%d", 12, 31, 2011]; NSLog(@"myObject1 == %@", myObject1); NSLog(@"myObject2 == %@", myObject2); UIScreen *s = [UIScreen mainScreen]; UIColor *c = [UIColor yellowColor]; }
An
NSError
object created by
initWithContentsOfURL:error:
must be
release
d.
See
Gone.
A
CGMutablePathRef
created with the function
CGPathCreateMutable
must be released with the function
CGPathRelease
.
See
Japan
and
Etch.
This is not an example of
alloc
and
release
,
but it’s analogous.
The objects created in the above
myMethod1
could be
release
d
in
myMethod1
because they were used nowhere else.
But the object created in the following
myMethod4
cannot be
release
d
in
myMethod4
because it is used outside of
myMethod4
.
And it would seem unfair to burden
myMethod3
with the responsibility for
releas
ing
the object, since
myMethod3
never called
alloc
,
new
,
or
copy
.
- (void) myMethod3 { MyClass *myObject = [self myMethod4]; NSLog(@"myObject == %@", myObject); } - (MyClass *) myMethod4 { MyClass *myObject = [[MyClass alloc] init]; return myObject; //Another method (myMethod3) is about to use this object. }
But
myObject
has to be
release
d
by
some
method.
This is accomplished by the
autorelease
in the following
myMethod5
.
The
autorelease
stores the address of
myObject
in an
autorelease
pool
object.
The
autorelease
pool
will subtract 1 from
myObject
’s
retainCount
at a future time,
when the pool is itself
release
d.
This will happen when the current event
(a touch or a shake)
has been completely handled.
If there are no events,
the pool will certainly be released when the app terminates;
see the
main
function of any app.
- (void) myMethod3 { MyClass *myObject = [self myMethod5]; NSLog(@"%@", myObject); } - (MyClass *) myMethod5 { MyClass *myObject = [[MyClass alloc] init]; [myObject autorelease]; return myObject; }
myMethod5
can be abbreviated to
- (MyClass *) myMethod6 { MyClass *myObject = [[MyClass alloc] init]; return [myObject autorelease]; }or even to
- (MyClass *) myMethod7 { return [[[MyClass alloc] init] autorelease]; }
An example of
autorelease
is in the
tableView:cellForRowAtIndexPath:
method in
this app.
If an object is used in only one method,
create and
release
the object (if it needs to be released)
in that method.
If the object is used by more than one method of a larger object,
make the object an instance variable of the larger object.
If an object is used by the methods of many different objects,
make the object an instance variable of the
application
delegate
(until later in the course).
If an instance variable that is a pointer to an object
is created with method other than
alloc
,
new
,
or
copy
,
it must be explicitly
retain
ed
when it is created.
(This is because methods that create objects other than
alloc
,
new
,
or
copy
usually
autorelease
the object that they create.)
An example is
littleObject2
below.
@interface BigClass: NSObject { LittleClass1 *littleObject1; LittleClass2 *littleObject2; } @end @implementation BigClass - (id) init { self = [super init]; if (self != nil) { littleObject1 = [[LittleClass1 alloc] init]; if (littleObject1 == nil) { [self release]; return nil; } littleObject2 = [LittleClass2 createAnObject]; if (littleObject2 == nil) { [self release]; return nil; } [littleObject2 retain]; } return self; } //release in reverse order. - (void) dealloc { [littleObject2 release]; [littleObject1 release]; [super dealloc]; } @end
In the above example,
the superclass is
NSObject
and the subclass is
BigClass
.
The
init
method of the subclass always
begins
by calling the
init
method of the superclass.
Conversely,
the
dealloc
method of the subclass always
ends
by calling the
dealloc
method of the superclass.
A call to an
init
returns
nil
if it cannot initialize the object that is being created.
A control is a
view
that is subclass of
UIControl
.
The most important method that a control inherits from this superclass is
addTarget:action:forControlEvents:
,
which accepts three arguments.
These arguments specify
UIControlEventTouchUpInside
for a
button,
or when
UIControlEventValueChanged
for a
switch
or
slider.
A control can have more than one target object,
and a given object can be the target of more than one control.
Because of the latter,
the method in the target object usually begins with an
if
statement to determine which control object triggered the call to the method.
Our first example is the
if
statement in the
buttonTouchUpInside:
method of class
ButtonView
in
Button
.
A
timer
can be thought of as a control (e.g., a button)
that is touched automatically after a given interval.
A
notification
center
can be thought of as a control
that is touched by another object.
Like the
addTarget:action:forControlEvents:
method of class
UIControl
,
the
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
method of class
NSTimer
and the
addObserver:selector:name:object:
method of class
NSNotificationCenter
accept a target object and a
selector.
The delegate of an object is like a target of a control.
At some time in the future,
the control will call a method of its target,
and the object will call a method of its delegate.
(More precisely,
the control does something that causes
somebody
to call the method of its target.
The method might not be called directly by the control.
Similarly, the object does something that causes somebody to call the method
of its delegate.)
A given object can be the target of more than one control,
and the delegate of more than one object.
A method in a delegate usually begins with an
if
statement to determine which object triggered the call to the method.
There are three differences between targets and delegates.
delegate
that is a pointer to the delegate.
Our first examples will be the
delegate
property of classes
UIApplication
,
AVAudioRecorder
,
AVAudioPlayer
,
and
UITextField
.
UIApplication
object only if it fulfills the
UIApplicationDelegate
protocol.
An object can be the delegate of an
AVAudioRecorder
object only if it fullfills the
AVAudioRectorderDelegate
protocol.
An object can be the delegate of a
UITextField
object only if it fulfills the
UITextFieldDelegate
protocol.
For more examples of protocols,
see
column 2.
AVAudioRecorder
object can trigger a call to the
audioRecorderDidFinishRecording:successfully:
and
audioRecorderEncodeErrorDidOccur:error:
methods of its delegate,
and a
UItextField
object can trigger a call to the
textFieldShouldReturn:
and
textFieldDidEndEditing
methods of its delegate.
A
UIButton
is simple enough to get by with a target (or several targets).
A
UIAlertView
(which may contain several buttons)
is complicated enough to require a delegate.
An object can have only at most one delegate.
If you need more than one,
you can get a similar effect using the
NSNotificationCenter
.
A
UIPickerView
has a
UIPickerViewDataSource
.
A
UITableView
has a
UITableViewDataSource
.
The
UITableViewDataSource
will often be the
UITableView
’s
UITableViewController
.
Similarly, a
UIDatePicker
has an
NSCalendar
.
A view controller renders five services to the view that it controls.
loadView
method of the)
view controller creates the view.drawRect:
message to its view when the device’s orientation changes.UITabBarController
,
and the navigation bar is visible only when the view controller is
underneath a
UINavigationController
.
A special-purpose view called a
UITableView
usually has a special-purpose view controller called a
UITableViewController
directly above it.
The
UITableViewController
acts as the
UITableView
’s
data
source
and
delegate,
in addition to acting as the
UITableView
’s
view
controller.
The views of an app are organized into a tree. Each view (except the window itself) contains a pointer to the superview that contains it. A view also has a (possibly empty) array of pointers to its own subviews.
A window cannot have a view controller. A view that is contained inside a superview cannot have a view controller.
Methods are called on five occasions:
applicationDidFinishLaunching:
method of the
application
delegate
is called.
When the iPhone Home button is pressed,
the
applicationWillTerminate:
method of the
application
delegate
is called.retainCount
drops to zero,
the object’s
dealloc
method is (usually) called.
touchesBeganWithEvent:
family of methods are called.addTarget:action:forControlEvents:
is called.
We consider a timer to be a control.drawRect:
method is called.
This was the first method where we were painfully aware that we did not know
who the caller was.
If
drawRect:
returned a value, what method would it be returned to?
UIApplicationMain
function in
main.m
nil
to the name of your application delegate class?
Info.plist
file?drawRect:
method by calling
[self setNeedsDisplay]
?retain
and
release
every instance variable that is a pointer to an object that was not created
by calling
alloc
,
new
,
or
copy
?
For example,
if an instance variable is a pointer to an array created by the method
arrayWithObjects:
,
you will need to
retain
and
release
it.
CGContextFillPath
or
CGContextStrokePath
?