Lesson 2: Creating Custom Classes in Objective-C

Properties, method declaration and implementation

--

In the previous lesson, we covered the concepts of object-oriented programming, class inheritance, Objective-C syntax and method creation and calling. Now that we’re familiar with the aforementioned, we can create our own custom classes. Let’s write some code.

Getting Started with Xcode 5.x

In order to write and build code, we’re going to have to familiarize ourselves with Xcode. If you haven’t downloaded Xcode, you can do so here—it’s completely free.

Open Xcode. The window that opens should look like this:

Click “Create a new Xcode project.” Upon doing so, select “Application” under Mac OS X, then “Command Line Tool.” When Command Line Tool is selected, click Next.

In the next screen, name your project (I’m calling mine “CustomClasses”). For Organization Name and Company Identifier, enter whatever you please—usually for testing, I just put my name. Make sure “Foundation” is chosen for Type, then click Next. Save your project to your Desktop, and click Create.

When your project opens, find the navigation table on the left, and open your “main.m” file. The contents of the file should look exactly like the code on the bottom.

This “main” function is where your program runs. When the compiler builds and runs your code, it looks for the “main” file. Once it has found it, it executes this function. As in C/C++, a return value of zero signifies a successful termination of the program. A nonzero return value, however, means something went wrong while the program was running. Right now, we’re not going to mess with this function. Instead, we’re going to work on creating a custom class.

Creating a Custom Class

In order to create a class, we primarily have to create a file. To do so, click File > New > File (or hit ⌘N). A selection view will drop, giving you more options. Go ahead an choose “Objective-C class” and hit Next.

For the class name, enter “Person.” Make it a subclass of NSObject. NSObject is the base class for all objects in Apple’s developer environment. It provides basic properties and functions for memory management and allocation. If you’re developing an iPhone application, every object you instantiate will inherit from NSObject. Click Next.

Make sure to save the file in your project folder. Ensure the group name matches your project name, and make sure your project name is checked under “Targets.” Click Create.

You should now have two new files in your navigation sidebar, “Person.h” and “Person.m.” Select the “Person.h” file.

A “.h” file is where the interface of a class is located, while the “.m” file is where the implementation of a class is located. The interface is where the class name, superclass, properties and methods are declared. Subsequently, the implementation is where these properties and methods are implemented and defined.

Interface

Currently, your “Person.h” file should contain the same code as below, except the comments on top. While there is not a lot of code in the file insofar, there is a lot of new stuff here.

Let’s take a look at line 9, where we see

#import <Foundation/Foundation.h>.

As in C and C++, any statement preceded by a hash sign (or pound sign/hashtag) is called a preprocessor statement, or a command that the compiler executes before executing other, normal code in the file. Because our superclass, NSObject, is declared in a file that is located in a library hidden somewhere in your computer, we have to import the library containing the file in order to access the class.

Moving to line 11, we’ll see the forward declaration of our class Person, followed by the declaration of its superclass, NSObject. Unlike other C-based languages, Objective-C uses “@” for declarative statements. You always declare a new class with @interface, followed by your custom class name, followed by a colon and ended with the name of your class’ superclass.

Finally, we’ll see @end on line 13. This tells the compiler that it’s done creating the interface for a new class. You must end all interfaces with @end.

Adding Properties

Now we can start writing some code. As aforementioned, in an interface file, we declare the properties and methods that belong to our custom class. Since our class is Person, we’ll declare properties and methods that are standard in human beings.

There are millions of ways to characterize a person, but for the purpose of simplicity, we’ll use only two for our programmatic properties—age and name. Since age is an integral number, we’ll declare it as int. In order to do so, we’ll write the following code between @interface and @end:

Declaring a property always starts with @property. After that, we declare the property’s attributes, or how the compiler will interact with the property—in this case, it’s an assign property, meaning we will assign the property a value. Following the aforementioned is the property type—in this case, an integer, because a person’s age is a positive, nonzero non-decimal value. Finally, we declare the name of the property. Here, it’s “age.”

Now, any instance of our Person class will have an integral property, or trait, of age. We can assign this property any integer value.

Moving on, we’ll add a name property. A name is usually a string, and Apple was kind enough to give developers an advanced class for handling large amounts of text, called NSString. NSString is automatically available because we imported its parent library on line 9.

NSString is an object, not a number, so it requires a bit more code to declare as a property:

As you can see, the declaration of our name property somewhat follows the same pattern as age. However, there are some minor differences: we now have two attributes in our parentheses—nonatomic and strong. These are complex Objective-C terms whose meanings must be understood in order to prevent crashes in complex applications. I won’t delve deeply into the definitions and applications of the two, but I will provide short explanations. Making a property nonatomic means it can be accessed more quickly, but will cause problems if you try to access it and change it simultaneously. Strong means you have direct control over the lifetime of the property—the compiler will not nullify it unless you specifically instruct it to. The opposite of nonatomic is atomic, while the opposite of strong is—you guessed it—weak. Below is a table of property attributes, accompanied by short explanations.

Upon the appendage of your age and name properties, your “Person.h” file should look something like this:

Now that we’ve given our class some basic properties, we can start declaring some methods.

Adding Methods

Declaring methods is simple—we declare them just as we learned in Lesson 1. Like properties, methods must be declared within the interface of a class (after @interface and before @end).

People have the ability to perform an unlimited range of actions, but we’re only going to declare three of them. The first will be a function we’ll use to initiate new instances of the Person class. The second will be a non-returning action that prints the person’s name to Xcode’s built-in debugging console, while the third will be a function we’ll use to return the person’s birth year. Let’s go ahead and declare them.

Our first method, -initWithAge:andName: is a convenience method that we will use to quickly allocate memory to a new instance of Person and initialize it with age and name values.

Our second method, -printName, will print out the Person’s name. Its return value is void because the function does not return anything.

The third method, -getBirthYear, will use the assigned age property to deduce and return the Person’s birth year. Its return value is int because we are returning a year, which is an integer.

The aforementioned are three instance methods—methods that are only called by instances of our class. Instance methods are always declared with a “-” (minus sign).

There is another type of method we can declare, called a class method. A class method is called not by instances of our class, but by the class itself. Class methods are always started with a “+” (plus sign), and are usually methods purposed for instantiation or retrieving class-wide information that is not affected by instance variables. We’ll declare two class methods now—one for quick instantiation and one for retrieving the species name of human beings.

Our first class method, +personWithAge:andName:, is for creating new instances of Person. Its return type is instancetype, which means the method returns an instance of the class. It is similar to our instance method -initWithName:andAge:, but the class method is quicker to write and requires less bracketization. The second class method, +speciesName, returns the general human species name in the form of NSString.

Our interface should now look like this:

We’re finished with our interface. Let’s move on to the implementation of our Person class.

Implementation

Mouse over to your navigation directory and choose “Person.m”—your implementation file. Your code should look like this:

That little yellow triangle on line 17 is a warning, notifying you that the methods you declared in “Person.h” are undefined. It’s not a problem now, but if you call an undefined method on an object, your program will crash.

On line 9 we see another preprocessor statement that imports the code from “Person.h.” We need that code in order to complete our Person class—you’ll notice that every time you create a class, you have to import your interface file into your implementation file.

On line 11, you’ll see that like declaring an interface, we declare an implementation with @implementation, followed by the class name, which in this case is Person. Additionally, as in our interface file, our implementation concludes with @end. All interfaces and implementations must conclude with @end.

Instance Methods

Let’s go ahead and define our awesome functions declared in our “Person.h” file. We’ll start with -initWithAge:andName:, as that function is essential for quick instantiation.

This one’s kind of complicated, so I’ll break it down.

On line 15, we see a new term:self. In Objective-C, self is a very frequently used term that represents a constant, generic instance of the class you’re implementing. In the Java and C++ languages, the equivalent of self is this. The purpose of line 15 is to call the -init method on Person’s superclass. By calling this method, all the code within NSObject’s -init method is invoked, and a new instance of Person is returned.

Line 16 contains an if-statement that determines if self is nil. If an object is nil, it is empty—all its properties have nil and NULL values, and calling methods on it will do nothing. However, since self was initialized on line 15, it is not nil, and thus the conditional statement yields true, allowing the compiler to execute the code within the conditional curly brackets. For more on Objective-C if statements, check out this tutorial.

Lines 18 and 19 use dot notation to assign values to the properties we declared in Person’s interface. Dot notation is very useful in Objective-C, as it allows you to access and change property values of objects, as in C++. After line 19, the age and name properties of self will have the correlated values defined in the function header.

On line 21, we conclude the function by returning self. Since the method we’re defining has a return value of id, we must return an object, which in this case, is self.

To wrap up this method, let’s create an instance of Person and assign it some values for age and name:

Notice the syntax for assigning NSString values: @””.

Above we created an instance of Person, allocated it some memory and initialized it with an age of 18 and a name of “William.” To be more efficient, we can reduce these three lines of code to just one by using a nested method:

Now let’s move on to our next instance method, -printName. This method will take advantage of a function that comes with the framework we imported in “Person.h” with our other classes— NSLog(NSString *format, …). NSLog() is a C function whose parameter is an instance of NSString that you can print to Xcode’s built-in debugging console. Since it’s a C function, it is invoked alone and not called by an object. NSLog() is a very handy tool that allows you to read various data in your program without interruption.

Line 26 shows NSLog() being invoked with a formatted string literal parameter. A formatted string literal is a string that contains a placeholder that will be replaced by the value that follows upon compilation. In Objective-C, these “placeholders” are written as a modulus sign (“%”) followed by a format specifier, which will vary depending on the type of the value that follows. For a complete list of format specifiers, click here.

Our third and final instance method, -getBirthYear, will calculate and return a year of birth based upon the age property.

On line 31, we declare an integer with the name currentYear and assign it the value of 2013. On line 32, we calculate the year of birth by subtracting the age property of self from the current year. Finally, on line 33, we return our calculated birth year.

To conclude our instance methods, let’s go ahead and invoke them.

Calling -printName will print “William” to Xcode’s debugging console. Calling -getBirthYear will calculate and return my birth year, based on my age and the current year.

Class Methods

Hang in there, we’re almost done—we just have to wrap up our implementation of Person by defining our class methods.

Our first class method, +personWithAge:andName: is an instantiation method that will allocate and initialize a new instance of Person and return it. Let’s write it.

See if you can think of a way to simplify these two lines of code to just one.

On line 38, we utilize our recently defined -initWithAge:andName: instance method to allocate and initialize william, an instance of Person. On line 39, we return that instance. Simple enough, let’s move on.

Our next and last class method, +speciesName, will return the name of our species.

We saved the simplest for last. On line 44, we assign the string value “Homo sapiens” to an instance of NSString, and return it on line 45. Implementation complete.

Using Our New Class

Now that we’ve created our own class, let’s test it! Open up your “main.m” file. Under the import statement, make a new line so we can import our “Person.h” file.

When importing libraries, use angle brackets. When importing your files, use quotation marks.

Importing the interface file gives us access to our newly created class. Mouse down to lines 17 and 18 and delete the two lines of code—we don’t need them. We’re going to use that space to test our custom Person class. On line 17, create an instance of Person and give it your age and name values, using the class method we defined for instantiation. On line 18, call the instance method -printName. On the next line, create an int variable and assign it the returned value of -getBirthYear. Finally, create a new line and see if you can print the formatted species name to the console using +speciesName and NSLog(). Once you’re finished, check below to see if you got it right!

The warning on line 19 notifies you that the variable “birthYear” is unused. To silence warnings such as this, use the variable on another line, in another context.

When everything looks good, go ahead and click the play button on the top left corner of your project window to run your program.

If everything ran correctly, you should see the console pop up on the bottom with your name, “Homo sapiens” and an exit code message.

Remember, an exit code of zero indicates your program terminated successfully.

Conclusion

You should now be able to do the following:

  • Create your own simple classes
  • Declare properties and methods
  • Define instance and class methods
  • Create instances of your classes and assign property values
  • Call your defined methods
  • Print data to the debugging console
  • Successfully build and run a program in Xcode

In the next lesson, we’ll take a look at creating more complex classes, and expanding our knowledge of properties. Thanks for reading, and code on!

Copyright 2013 William Harrison Development. All Rights Reserved.

--

--