How to Use Properties to Better Encapsulate Your Code?   Leave a comment


Download ExampleDifficulty: easy moderate challenging

In this article I will explore the usage of properties in order to simplify, bulletproof, and even better encapsulate your code. These are not merely slogans. By the advanced use of properties [and categories], you’ll see how we save coding time, write less implementation details, make our code less error-prone, and make other classes who use our class more independent.

 

The standard use of properties: to create fields your and other classes can modify and access was thoroughly explained in my previous post: What are Properties and how do they work?. Instead, in this article, we’ll deal with three distinct types of variable usages, whose names I made up, and standardize their implementation using properties. We’ll start with Internal Variables – variables that are to be used only within our implementation, we’ll continue to Seemingly Read-Only Variables – variables that are read-only to all classes but our implementation – and lastly, we’ll discuss General-Specific Variables – variables that are exposed to other classes as a superclass of what they are, whom merits will be explained. We trouble ourselves with coding these cases using properties in order to exclude any implementation details from our code, for instance, by not using retain or release at all. Plus, since the memory options of properties (retain, assign, and copy) are easily changeable from one to another, it will allow for faster and less-buggy code changes later.

 

Properties Usage for Internal Variables

Many of the variables we use in our code are internal to our classes: other classes should not be aware of them, let alone access them. So, why should we even consider using properties in such cases? The simple answer is: to write less code, as usual, and to avoid bugs, particularly in our memory management. In the attached source code, such a variable is identification in class Person.

 

Since we’re considering internal variables, we would want to have the least impact on our .h, if any at all; we will try to put all our code into the .m file instead. First, we would want to declare a property as follows:

@property (retain, readwrite) NSString* identification;

But this usually goes into the @interface of our .h file. Leaving it in the .h file will allow users of our class to access it. Instead, in order to put it into the .m file, we use the following code:

@interface Person () //a private category

 @property (retain, readwrite) NSString* identification;

@end

This code adds a private category to our class in question, Person, and modifies the class’ declaration by adding a getter and setter methods. Since none of this is in the .h file, other classes are unaware of the two new methods. Trying to use them from other classes will generate the following problems:

    aPerson.identification=@""; // generates an error: object cannot be set – either readonly property or no setter found

    [aPerson setIdentification:@""]; // generates a warning: ‘Person’ may not respond to ‘-setIdentification:’

 

Again, this is what we wanted – for other classes to not know about our internal variable; and this happens because the classes just #import "Person.h" and as such are not aware of the property. As oppose to other classes, your implementation code is written in a separate .m file, where the property is declared. So, you can treat the internal property as any other property in your own class implementation, with the following useful paradigms:

*  To set the property (== to own the object, since we use the retain option), you should use:

     self.identification=[NSString stringWithFormat:@"%l", [name hash]]; // the retain property will own the given object

*  To access the data, you can just access the variable, such as:

    NSLog(@"my identification is: ‘%@’", identification);

*  And finally, to discard your reference appropriately (by releasing it), you can simply use:

    self.identification=nil;

From the examples it is clear to see that by using a property to hold a piece of data we need not worry about its memory management details at all. Also, if you simply change the retain directive to assign or copy your code will need not be altered – it’s that simple.

 

Remember that even with a privately declared property, as is always the case with properties, somebody has to implement the getter and setter. And, as usually the case with properties, you can just include a @synthesize directive in your .m file, in its @implementation section as follows:

@implementation Person

@synthesize identification;

This synthesizes the setter and getter, and uses the memory management scheme we specified in the property declaration (retain).

 

Lastly, keep in mind that the use of a property requires a member variable to store the reference to the object in question. As was shown in the previous post, a variable declaration goes into the .h file. Although this wasn’t such a big deal for the other post, it is now; here, we try to hide our implementation from the other classes and thus, the .h file. There is an option to just omit the variable declaration and have the @synthesize create it; however, as stated in the other post, this is not supported on all devices; technically, it is only supported on “modern runtimes” [sic]; but even with my modern runtime, I noticed some peculiarities when you expect the @synthesize to create variables for you; and so I am reluctant to omit such variable declaration just yet, leaving my .h file not entirely empty of implementation details, as shown:

@interface Person : NSObject {

    NSString* identification;

If you follow my footsteps by including the variable declarations in your .h files, don’t be discouraged by the lack of full concealment, because having the declaration doesn’t change the way other classes can, or cannot, interact with your class.

 

Properties Usage for Seemingly Read-Only Variables

In this case, we consider allowing other classes to read our variable, but not to change it. Obviously, our implementation still requires changing the variable for its normal operation. We would prefer, again, to use properties, as oppose to manually coding and handling of memory management. This case corresponds to the variable name in class Person in the attached source code.

 

We achieve our goal of having a seemingly read-only variable by using two different property declarations. First, we declare a read-only property in the .h file:

@property (retain, readonly) NSString* name;

and then a read-write one in the .m file, using a private category:

@interface Person ()

@property (retain, readwrite) NSString* name;

@end

Luckily for us, the compiler knows that the latter declaration supersedes the former one and does not conflict with it.

 

Our class’ users, who import just the .h file, see this property as read-only and can use it to query its value:

    NSLog(@"Just created a person named %@", aPerson.name);

but would raise an error if they try to set it:

    aPerson.name=@""; // generates an error: object cannot be set – either readonly property or no setter found

 

As for our class, since our .m file contains the other, more elaborate property declaration, we can set the variables as needed:

@implementation Person

– (id) init

{

    if (self=[super init]) {

     self.name=[NSString stringWithFormat:@"Person %d", rand()];

 

Properties Usage for General-Specific Variables

In this last case, we consider an object that we want to be used by other classes as one type but for our class as another. The justification for this is because mutable classes are subclasses of their immutable representations, which kind of brings to mind the seemingly read-only variables from the preceding section. For instance, let’s consider an NSArray; we would want our class to be able to set values on the array, and so would like to reference an NSMutableArray; but for the class’ users, we prefer to export the object as a simple immutable NSArray. As we’ll see next, by using properties, we can achieve this, but only because NSMutableArray is a subclass of NSArray. The same is true for NSString and NSMutableString, NSSet and NSMutableSet, NSDictionary and NSMutableDictionary and others. Keep this fact in mind when you write your own generic classes. My source code example for this kind of variable is people in class People.

 

To export the object as a read-only, immutable object, in your .h file you would use:

@property (readonly, retain) NSArray* people;

This will allow other classes to access the people using NSArray’s standard methods, without your need to manually export similar methods of your own, for example:

    NSLog(@"There are %d people in total", [somePeople.people count]);

    for (Person* thePerson in somePeople.people) {

     NSLog(@"A person named %@", thePerson.name);

    }

 

But in order for the class itself to be able to modify the content of the array, it needs to actually reference an NSMutableArray. So that is how our instance variable should be declared in the .h file – to be of the specific subclass:

@interface People : NSObject {

    NSMutableArray* people;

}

@property (readonly, retain) NSArray* people;

As you see here, people is actually an NSMutableArray, so we can use its mutable methods in our .m file, for example:

    [people removeObject:aPerson];

But this only works if we use the variable directly, as oppose to trying to use the property:

[self.people removeObject:aPerson]; // generates a warning: ‘NSArray’ may not respond to ‘-removeObject:’

And that’s exactly what’s happening when other classes try to use our secretive NSMutableArray.

 

And, obviously, we would still prefer to use the property’s automatic memory management and so in our .m file will include:

@interface People ()

@property (readwrite, retain) NSArray* people;

@end

and therefore we can use:

– (id) init

{

    if (self=[super init]) {

     self.people=[NSMutableArray array]; // referencing our mutable object

 

 

– (id) initWithArrayOfPersons:(NSArray*)arrayOfPersons

{

    if (self=[super init]) {

     self.people=[NSMutableArray arrayWithArray:arrayOfPersons];

 

If you are vigilant, you might see that this mechanism works correctly because the property retains the exact same object that we allocated and referenced to the setter method, an NSMutableArray.

 

Even though quite complicated and requires some manual memory management, there is an extension to the preceding mechanism, which allows other classes to modify people. However, instead of allowing others access to the NSMutableArray and so enable them to remove, add, and change elements at will, we can make the property declaration to be read-write and allow setting it. This less obvious declaration allows us to limit the way other classes modify our list of people to a single point: setPeople:, still allowing you to maintain your class invariants and constraints by implementing this method yourself. I will not dwell on this now, perhaps in a separate post later. At any rate, you can check out the source code for such an example, in particular class MutablePeople.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: