Summary of the Rules

  1. How the Story Begins
  2. Memory Management
  3. Inheritance
  4. A Control and its Target(s)
  5. An Object and its Delegate
  6. An Object and its Data Source
  7. A View Controller and its View
  8. When are Methods Called?
  9. Things I Often Forget

How the Story Begins

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.

  1. The main function is always the first function to be executed. It calls the UIApplicationMain function.

  2. The 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>. You have to write this subclass yourself.

  3. Your application delegate must contain at least two instance variables (internal organs). The first instance variable is the view object, of a subclass of class UIView. You have to write this subclass yourself. The second instance variable is the window object, always of class UIWindow.
    	SubclassOfUIView *view;
    	UIWindow *_window;
    

  4. The first message sent to the application delegate (or at least the first message of which we are aware) is application:didFinishLaunchingWithOptions: (or applicationDidFinishLaunching: in older versions). This method should create the view and the window (in that order: the innermost object first). Then it should add the view to the window, and make the window visible. For the time being, an application will have exactly one window and one or more views in the window. (Later in the course, the application delegate will create a view controller instead of a view, and the view controller will create the view.)

  5. The last (or next-to-last) message sent to the application delegate is 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.)

  6. The only thing we see is the view. Although the window is technically “visible”, it is occupied entirely by, and obstructed by, the view.

  7. The first message sent to the view is 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.

  8. The drawRect: method of the view should draw the view. Do not be unnerved because we never see who calls drawRect:. After all, we never saw who called the application:didFinishLaunchingWithOptions: method of the application delegate.

Memory Management (replaced by ARC)


I am a Ford, not a Lincoln.

We have a memory manager, not a garbage collector.

The retainCount

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);

dealloc before death

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.

Pairs of increments and decrements

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 retained 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 exception: NSError

An NSError object created by initWithContentsOfURL:error: must be released. See Gone.

A seeming exception: CGMutablePathRef

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 autorelease pool

The objects created in the above myMethod1 could be released in myMethod1 because they were used nowhere else. But the object created in the following myMethod4 cannot be released in myMethod4 because it is used outside of myMethod4. And it would seem unfair to burden myMethod3 with the responsibility for releasing 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 released 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 released. 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.

An instance variable that points to an object must be retained and released

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 retained 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

Inheritance

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 and its Target(s)

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

  1. what object the control should send a message to. This object is called the target of the control.

  2. what message the control should send to this object (i.e., what method to call). This argument is always a selector.

  3. when to send the message. Typical occasions would be when 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.

An Object and its Delegate

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.

  1. A control can have more than one target, but an object that can have a delegate can have only at most one delegate. An object that has a delegate always contains a property named delegate that is a pointer to the delegate. Our first examples will be the delegate property of classes UIApplication, AVAudioRecorder, AVAudioPlayer, and UITextField.

  2. Any old object can be the target of a control. But an object can be a delegate only if it fulfills a set of requirements called a protocol. For example, an object can be the delegate of the 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.

  3. A control can trigger a call to any old method of its target(s). But an object that has a delegate can trigger a call to only certain methods of its delegate. The method must belong to the protocol fulfilled by the delegate. For example, an 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.

An Object and its Data Source

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 and its View

A view controller renders five services to the view that it controls.

  1. The (loadView method of the) view controller creates the view.

  2. The view controller can send the drawRect: message to its view when the device’s orientation changes.

  3. The view controller gives its view a black tab at the bottom and a brushed aluminum navigation bar at the top. The black tab is visible only when the view controller is underneath a UITabBarController, and the navigation bar is visible only when the view controller is underneath a UINavigationController.

  4. Instead of trying to talk to distant objects, a view can address all of its remarks to its view controller. This will allow the view to concentrate on local issues such as background color, foreground color, font size, etc. To make it easy for a view to send a message to its view controller, a view is often given an instance variable which points up to the controller.

  5. A view controller can yield the floor to another view controller. The other view controller will then display its own view.

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.

Which views can have a 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.

Properties that may not be visible

  1. A view controller can have a title property. If the view controller is controlled by a tab bar controller or by a navigation controller, the title will be visible. Otherwise, the title is not shown.

  2. A view controller can have a tab bar item property. If the view controller is controlled by a tab bar controller, the tab bar item will be visible. Otherwise, the tab bar item is not shown.

  3. A view controller can have a navigation item property. If the view controller is controlled by a navigation controller, the navigation item will be visible. Otherwise, the navigation item is not shown.

When are Methods Called?

Methods are called on five occasions:

  1. When the app is launched, the applicationDidFinishLaunching: method of the application delegate is called. When the iPhone Home button is pressed, the applicationWillTerminate: method of the application delegate is called.

  2. When an object’s retainCount drops to zero, the object’s dealloc method is (usually) called.

  3. When a touch arrives, the touchesBeganWithEvent: family of methods are called.

  4. When a control is tapped, dragged, etc., the method designated by addTarget:action:forControlEvents: is called. We consider a timer to be a control.

  5. When a view is born, and when the iPhone’s orientation changes (if there is a view controller), the view’s 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?

Things I Often Forget

  1. [This is necessary only in Xcodes older than version 4.2.] If all you see is a white screen, did you change the the fourth argument of the UIApplicationMain function in main.m from nil to the name of your application delegate class?

  2. [This is necessary only in Xcodes older than version 4.2.] If all you see is a white screen, did you remember to remove the “Main nib file base name” line from the project’s Info.plist file?

  3. If you don’t see one of your views, did you remember to add your view to its superview?

  4. If you see your view but it doesn’t update itself, did you remember to trigger a call to its drawRect: method by calling [self setNeedsDisplay]?

  5. [This is necessary only if you have not checked the “Use Automatic Reference Counting” checkbox when you created the project.] If your app blows up unpredictably, did you remember to 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.

  6. If the graphics in your path do not get drawn, did you remember to call CGContextFillPath or CGContextStrokePath?

  7. If the methods of a delegate are not being called, did you remember to give the original object a pointer to the delegate?