Lesson 3: Expanded Class Creation and More About Properties
Getters, setters, privatization and instance variables
In Lesson 2, you learned how to create and implement simple custom classes. In this lesson, we will expand our knowledge of class creation with the addition of privatization, synthesizing, getters and setters. Although this is a shorter lesson, it is more complex and thus requires more time to master.
We’ll be coding in the same project as in the previous lesson. If you haven’t created the project, do so by following the instructions in Lesson 2.
Properties Expanded: Getters, Setters and Instance Variable Properties
As you learned in the previous lesson, properties are very useful for characterizing objects and relating them to objects in the real world. The usefulness does not stop there, however; with getters and setters, you are able to control both the retrieval of your properties and the assignment of new values to your them, respectively. In order to fully understand getters and setters, we must first discuss the nonatomic property attribute.
Property Attributes: Nonatomic vs. Atomic
In order to utilize getters and setters to the maximum extent, we must know whether to assign the nonatomic or atomic attribute to a property. When methods are called in Objective-C, they are put on a thread, which is simply a function queue. If an atomic property’s getter is called on one thread while its setter is running on a different thread, the data will be skewed, and you will be unable to get/set an accurate value. Put simply, if you wish to implement specific getters and setters for a property, ensure it has the nonatomic property, as properties are atomic by default. Otherwise, don’t worry about it.
Getters
Getters are automatic methods that are created when you add properties to your classes. They allow you to impement how you access your properties and their values.
For example, with a getter you could increment an integer each time the value of that specific property is accessed. Let’s try that.
To commence this process, we’re going to open our “Person.h” file and declare an interface instance variable of integer type. An interface instance variable is an internally accessible, non-property variable that you can use in your class implementation. You declare these by adding curly brackets under @interface Person : NSObject.
This integer, ageAccessCount, has a default value of zero and is accessible everywhere within Person’s implementation. However, it is not accessible anywhere else.
Now that we have this declared, we can implement a getter for our age property. As aforementioned, getters are automatically created when you create properties. These getters methods, by default, have the same name as their associated properties. To set your own getter method name, simply add the attribute getter=methodName to your property. Remember to add the nonatomic attribute to the age property because you are adding a specific getter method.
Now, for every instance of Person, there is a new instance method for retrieving the age property, called -retrieveAge. The instance method for retrieving the name property, however, remains -name.
Once you assign a new getter to a property, you must implement the method in your class’ implementation file. Let’s go ahead an do that now.
Now, each time you access the age property (whether by dot notation or method calling), the integer variable ageAccessCount will increment.
Setters
Like getters, setters are automatic methods added to your class upon the creation of a property. The function of setters, as you might guess, is to control the value assignment of your properties. Let’s try to create a setter method for our name property that stores the previous value of the name property in an instance variable.
First, we must create the interface instance variable. Go ahead an add an instance of NSString to Person’s “.h” interface. Just as ageAccessCount had a default value of zero, oldName has a default value of nil.
Now let’s add a setter to our name property. In order to do so, we must add setter=setterMethodName: as an attribute. As you may know, the colon at the end of the method name is there because setters take parameters.
Just like when we added a getter to our age property, we must now implement our new method. Hop over to the implementation file to do so. The objective is to store the old name value into the instance variable of NSString we previously declared before assigning a new value to our name property.
Now that we have our new getter and setter methods, let’s test them out.
Instance Variables of Properties
There is a third and final addition when you create a new property: the instance variable of the property. This is an instance variable whose title is an underscore (“_”) followed by the name of the property. For example, our age property’s instance variable would be _age. The purpose of these additional variables is to primarily allow access to readonly variables within implementation, and secondarily allow the getting and setting of properties without the use of getter and setter methods or dot notation.
Property and Method Privatization
When creating a class in Objective-C, you might want to add an object or number property that is accessible only within the implementation of the class. This cannot be accomplished with “.h” interface properties—those are universally accessible. However, you can create private data for the use of your class’ implementation through the use of “.m” interface properties.
“.m” Interface Properties
The best way to add “private” variables to your class is through the declaration of properties in the implementation file. Let’s go ahead and add a private property that we can only access within our implementation.
Open up your “Person.m” file and enter a few new lines between the import statement and your @implementation declaration. A useful feature of Objective-C is the ability to append the interface of a class in its implementation file. Any properties or methods declared in this interface is only accessible within the implementation of the class—not anywhere else. To add another interface, simply code another pair of @interface and @end—but this time, don’t add the superclass. Instead, type an empty pair of parentheses after the class name.
Now we’re going to add two properties—height and weight. These will be a private, readonly property of integer type. We will privately use these properties in order to calculate a Person’s BMI, a method we’ll later add to “Person.h”.
When you add a readonly property to a class, remember that you must either implement a getter method in your implementation or assign the property a value somewhere within the class’ implementation.
To start us off, let’s add two methods to our “Person.h” file. Navigate there and add the composite, void, setter function -assignValues:(int)heightInInches andWeight:(int)weight. After that, add the float-returning instance method -calculateBMI.
The first method, once implemented, will assign our new properties with the respective values in the function header. The latter will calculate and return the Person’s BMI, based on the aforementioned properties.
In Person’s implementation file, define the value that assigns height and weight. See if you can write it without looking at the provided snipped below.
We’ll use the standard English BMI formula for our BMI calculation method.
Okay, we’ve written a lot of code. Let’s test this in our “main.m” file. To really know if our code works, we should make our interface instance variables public. Instance variables are made public by preceding them with @public, and are subsequently accessed by using pointer notation, or “->”.
Now that our instance variables are public, we can test and see if the code works. I’ve added a few lines to our main function:
If it works like we want it to, the access count should be 3, while the old name should be “Billy”; Go ahead and build and run.
Conclusion
You should now be familiar with the following:
- The difference bewteen nonatomic and atomic properties
- Creating custom getter and setter methods for properties
- Creating private interface properties
- Accessing instance variables of properties
- Changing the values of readonly properties
In the next lesson, we’ll discuss three fundamental Foundation classes: NSString, NSArray and NSDictionary. I hope you enjoyed this lesson, and code on!
Copyright 2013 William Harrison Development. All Rights Reserved.