Create a class, and an object of that class

A class is the blueprint for objects of a specific type. It lists the instance variables (internal organs) and properties (externally visible organs) of the objects, the messages to which one of these objects can respond (the methods that belong to the object), the messages to which the class as a whole can respond (the methods that belong to the class), and the protocols (obligations) assumed by the objects.

The name of a class always begins with an uppercase letter. The classes the we create will begin with one uppercase letter. The classes that have already been created by Apple begin with two uppercase letters: NS, UI, etc. Let’s create a class named Date. The file main.m demonstrates what we hope to be able to do with this class and with objects of this class that we instantiate (create).

The description of a class is written in a pair of files, in this case named Date.h and Date.m. The Date.h file comes first; the h stands for “header”. The Date.m file comes second; the m stands for “implementation”.

This class Date serves only to demonstrate how to write a class. If we really wanted a useful object that would hold a date, we would instantiate an object of the class NSDate that has already been written for us. It’s a much better class.

Create the project

  1. Create a project just like the previous one, with three changes:
    1. The project (product) should be named Date.
    2. The class prefix should be Date.
    3. Do not insert the statement(s) (NSLog, etc.) into the main function.

    Product Name: Date
    Organization Name: John Doe     (will appear in copyright notcies)
    Company Identifier: edu.nyu.scps     (a backwards hostname, e.g., com.qconsf)
    Bundle Identifier: edu.nyu.scps.Date
    Class prefix: Date     (same as the product name)
    Devices: iPhone     (for the time being)

    Do not check Use Core Data.
    Press Next.

  2. Save the project on the Desktop for now.
    Uncheck Source Control.
    Press Create.
  3. Create a class named Date. In the Xcode Project Navigator, highlight the folder Date.
    File → New → File…
    In the upper left, select Cocoa Touch.
    In the upper right, select Objective-C class.
    Next
    Class: Date     (with an uppercase D)
    Subclass of: NSObject     (with an uppercase NSO)
    Next
    Create
    The Date folder in the Xcode Project Navigator should contain a new pair of files, Date.h and Date.m.
  4. Insert the statements shown here into the Date.h and Date.m files.
  5. In the main.m file, #import your header file Date.h. In the body of the main function, insert the statements shown here to instantiate several Date objects and call their methods.

Source code in Date.zip

  1. main.m
  2. Class Date, described in the files Date.h and Date.m.

The empty Date.h and Date.m

The two new files should initially contain the following.

//
//  Date.h
//  Date
//
//  Created by <Username> on 10/20/13.
//  Copyright (c) 2013 <Organization Name>. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Date: NSObject

@end
//
//  Date.m
//  Date
//
//  Created by <Username> on 10/20/13.
//  Copyright (c) 2013 <Organization Name>. All rights reserved.
//

#import "Date.h"

@implementation Date

@end

The interface of class Date in date.h

Class Date is a subclass of class NSObject, i.e., is derived from class NSObject. This means that an object of class Date (known henceforth as “a Date object”) has everything that an object of class NSObject (known henceforth as “an NSObject object”) has, plus more.

For example, a Date object contains all the instance variables (internal organs) that an NSObject contains. In addition, a Date object also contains the three instance variables in the {curly braces} of the class interface in the file Date.h.

A Date object can receive the 13 messages implemented by the 13 methods marked by a leading minus sign in Date.m. In addition, it can also receive all the messages that can be received by any NSObject object (such as retainCount). init and description are expanded versions of methods of the same name inherited from class NSObject, and therefore do not need to be declared in the class interface. in Date.h. The other 11 methods must be declared in Date.h.

Class Date can receive the message yearLength. This message is not sent to any object. It is sent to the class itself, and is therefore marked with a plus sign.

Class methods and instance methods

A method called in response to a message sent to a class is referred to as a class method. The declaration (in Date.h) and definition (in Date.m) of a class method are marked with a plus sign. A method called in response to a message sent to an object is referred to as an instance method. The declaration and definition of an instance method are marked with a minus sign.

Getters and setters

The year, month, and day methods of class Date are called getters because they get us the value of an instance variable. They take no arguments, and return int since the instance variables are ints. The data type of the return value is written in parentheses in front of the name of the method.

The setYear:, setMonth:, and setDay:, methods of class Date are called setters because they let us change the value of an instance variable. They each take one argument of type int. The data type of an argument is written in parentheses after the colon, followed by the name of the argument.

The implementation of class Date in Date.m

The file date.m contains the implementation of class Date, including the definition (body) of each method of class Date. An implemention file always begins by #importing the corresponding header file.

A method of one object can easily call another method of the same object. (An object refers to itself as self.) For example, next: calls next, and next calls monthLength. Look for the keyword self immediately after a left square bracket.

Birth and death

As described in our incantation, the init and initWithMonth:day:year: methods of the subclass (class Date) begin by calling the init method of the superclass (class NSObject).

If the call to the init method of the superclass was successful (i.e., if it did not return nil), the init method of the subclass then puts values into the instance variables (year, month, day) of the Date object that is being born.

If the entire instantiation was successful, init returns the address of the newborn object of class Date. Otherwise, it returns nil.

“Bitwise or”

The init method of class Date performs arcane negotiations with the operating system to discover today’s date. The value of unitFlags in this method is 28. But don’t think of it as 28 in decimal. Think of it as 11100 in binary. Better yet, think of it as “yes, yes, yes, no no”. The three yesses (When Harry Met Sally was filmed at Katz’s Delicatessen here on Houston Street) mean that we want to break today’s date down into year, month, day. The multitude of noes mean that we have no interest in breaking it down into hours, minutes, seconds, etc. We combine the three yesses with the “bitwise or” operator | (a vertical bar). For these three values, “bitwise or” just happens to do the same thing as plain old addition.

NSYearCalendarUnit    00000000000000000000000000000100   (decimal  4)
NSMonthCalendarUnit   00000000000000000000000000001000   (decimal  8)
NSDayCalendarUnit     00000000000000000000000000010000   (decimal 16)
---------------------------------------------------------------------
unitFlags             00000000000000000000000000011100   (decimal 28)

Press the Run Button

2014-06-08 15:40:17.976 Date[1364:60b] A year has 12 months.
2014-06-08 15:40:17.986 Date[1364:60b] Today is 6/8/2014.
2014-06-08 15:40:17.988 Date[1364:60b] Today is 6/8/2014.

2014-06-08 15:40:17.989 Date[1364:60b] Today is day number 8 out of 30 in month number 6.
2014-06-08 15:40:17.990 Date[1364:60b] Today is day number 8 of the month.
2014-06-08 15:40:17.991 Date[1364:60b] Today is day number 8 of the month.

2014-06-08 15:40:17.993 Date[1364:60b] The second day of this month is 6/2/2014.
2014-06-08 15:40:17.993 Date[1364:60b] Independence Day was 7/4/1776.
2014-06-08 15:40:17.997 Date[1364:60b] America was one month old on 8/4/1776.
2014-06-08 15:40:18.068 Date[1364:60b] Application windows are expected to have a root view controller at the end of application launch

Apply a dot to an object

When we apply a dot to a structure, we get or set the value of one of the fields of the structure.

	CGPoint p = CGPointMake(0, 0);
	CGFloat x = p.x;	//Get the field.
	p.x = 10;		//Set the field.

When we apply a dot to an object (more precisely, when we apply a dot to a pointer to an object), it looks like we get or set the value of one of the instance variables of the object.

	Date *today = [[Date alloc] init];
	int y = today.year;	//Looks like we're getting the instance variable.
	today.year = 2000;	//Looks like we're setting the instance variable.

But the last two statements actually do the following.

	int y = [today year];	//Call the getter method.
	[today setYear: 2000];	//Call the setter method.
The dot operator, together with a pair of appropriately named methods, create the illusion that we can get and set the instance variables of an object just like we get and set the fields of a structure. In reality, the dot operator of an object is a shorthand for calling a getter or setter. A dot that attempts to read an instance variable named varname automatically calls the method named varname. A dot that attempts to write an instance variable named varname automatically calls the method named setVarname:.

The methods called by the above code are declared in the Date.h file

- (int) year;			//getter
- (void) setYear: (int) y;	//setter

and defined in the Date.m file:

- (int) year {			//getter
	return year;
}

- (void) setYear: (int) y {	//setter
	year = y;
}

Our getters and setters are simple one-liners because our instance variables are ints. More complicated instance variables would require more complicated getters and setters.

Turn an instance variable into a property

Instead of declaring and defining the getter and setter ourselves, we can make the computer behave as if we had declared and defined them. Remove the above declarations of the methods year and setYear: from Date.h. Remove the above definitions of the methods year and setYear: from Date.m. Insert the following statement into the class interface in Date.h between the } and the @end.

@property (nonatomic, assign) int year;

Insert the following statement into the class implementation in Date.m immediately after the line @implementation Date. It creates a property named year from an instance variable named year.

@synthesize year;

An instance variable that we can access with the dot notation thanks to a getter and setter that were written for us by @property and @synthesize is called a property.

If we don’t write the @synthesize line in the current Xcode, it will behave as if we had written the following (in some versions of Xcode). It tries to create a property named year from an instance variable named _year.

@synthesize year = _year;

Things to try

  1. Will the initWithMonth:day:year: method of class Date placidly accept an invalid month number such as 13 or –1? If so, correct it by changing
    		month = m;		//assigns directly to the instance variable
    
    to
    		self.month = m;		//means [self setMonth: m];
    
    Verify that initWithMonth:day:year: now prints an error message with NSLog when it receives an invalid month. Error check the day too, but not the year.
  2. If the year is already equal to 2147483647 (the maximum int value, 231 – 1), have the next method of class Date print an error message with NSLog instead of blindly executing the ++year. Even better, #import <limits.h> (with angle brackets instead of double quotes, because this header file is in a far-off directory) and write the macro INT_MAX (all uppercase, one underscore) instead of 2147483647.
  3. In the init and initWithMonth:day:year: methods of class Date, we can abbreviate
    	self = [super init];
    	if (self != nil) {
    
    to any one of the four following fragments. Which is clearest?

    explicit comparison to nil implicit comparison to nil
    assignment in
    separate statement
    	self = [super init];
    	if (self != nil) {
    
    	self = [super init];
    	if (self) {
    
    assignment in
    same statement
    	if ((self = [super init]) != nil) {
    
    
    	if (self = [super init]) {
    
    

    In all four cases, the if statement will be true if the call to the init method of the super object was successful. In that case, we can go ahead and store values into the instance variables year, month, and day that were introduced in the subclass. An init method always returns the pointer self, which points to the object that was just instantiated.
  4. Create methods prev and prev: for class Date, analogous to next and next:. Call them in the main function to demonstrate that they work.
  5. Add an instance variable that is a pointer to an object:
    	NSString *reminder;	//inside the {curly braces} in Date.h
    
    	//Initilize the reminder to point to the empty string
    	//in the methods init and initWithMonth:day:year: in Date.m.
    	reminder = @"";
    
    	//The string returned by the description method in Date.m
    	//should display the reminder.
    
    	return [NSString stringWithFormat: @"%d/%d/%d %@",
    		month, day, year, reminder];
    
    An instance variable that is a pointer to an object requires the word strong instead of assign when you turn it into a property.
    //in Date.h
    @property (nonatomic, strong) NSString *reminder;
    
    //in Date.m
    @synthesize reminder;
    

    In the main function, call the setter.

    	//After you create independenceDay, but before you print it with NSLog.
    	independenceDay.reminder = @"Fireworks tonight";
    
  6. Do not hardcode the number 12 into the yearLength method in Date.m. Change the definition of this method to the following.
    + (int) yearLength {
    	NSCalendar *calendar = [[NSCalendar alloc]
    		initWithCalendarIdentifier: NSGregorianCalendar
    	];
    
    	//What is the range of months in the year that contains today?
    
    	NSRange range = [calendar
    		rangeOfUnit: NSMonthCalendarUnit
    		inUnit: NSYearCalendarUnit
    		forDate: [[NSDate alloc] init]
    	];
    
    	return (int)range.length;  //How many months are in the range of months?
    }
    
  7. [This is not homework. You don’t have to do it.] Change NSGregorianCalendar to NSHebrewCalendar, NSIslamicCalendar, etc. For calendars in which a year does not always have the same number of months, you will have to change yearLength from a class method (marked with a plus sign) to an instance method (marked with a minus sign).