What are Properties and how do they work?   2 comments


Difficulty: easy moderate challenging

Properties are a way for programmers to simplify the use – or to perhaps minimize the misuse – of handling instance variables, especially in regard to how their memory management is being done. Although properties support scalar C data variables such as BOOL, int, etc., their strength exhibits while dealing with NSObjects, when memory management becomes an issue. If you ever noticed properties and didn’t quite understand what the heck, than I would start by saying that properties are not so complicated as they seem at first and there is no voodoo happening that makes them work – they are merely a different syntactic notation for what you normally code in Objective-C. Properties modify two aspects of your code: 1) they streamline how users of a class access its variables, 2) they simplify memory management for classes, by minimizing or even omitting implementation details of the variables’ memory management. If the previous sentence or even the entire paragraph were difficult to understand, read it again after finishing the article and then see if it makes sense.

 

Let’s first focus on the former part – how do we use properties? At first, well, you need to declare them. Since this relates more to the latter part (memory management), we’ll dwell upon it later. For now, then, let’s imagine we have a property configured for an NSString* name in a class Person.

When we want to set its value, we use a dot notation, as noted in the second line of the following code:

    celeb=[[Person alloc] init];

     celeb.name=[NSString stringWithFormat:@"%@ the %@", @"Eric", @"2nd"];

This populates the reference of the variable name in our Person‘s instance, celeb, to point to a new string created using stringWithFormat:, which contains the value of @"Eric the 2nd". But how does it do that, you ask? Well, the compiler actually modifies your code to be:

    celeb =[[Person alloc] init];

    [celeb setName:[NSString stringWithFormat:@"%@ the %@", @"Eric", @"2nd"]];

This second form looks much like your normal Objective-C code. The use of a property here just synthetically applied a setter method. This method – setName – expects to receive an NSString* and then associates the name instance variable with it. As to the variable’s memory management, the explanation will follow soon enough.

 

Getting the value of an existing property is quite similar:

    NSLog(@"Our local celebrity is: %@", celeb.name);

 And this is actually rewritten under the hood as:

    NSLog(@"Our local celebrity is: %@", [celeb name]);

This is a use of a getter method, named name, which doesn’t receive any variables but returns an NSString* in return.

 

Properties not only unify getter and setter methods’ names, but also their declaration and their implementation. This standardization is achieved by putting placeholders in your header and implementation files instead of the actual method declarations and their implementation.

So, instead of declaring the getter and setter methods yourself in the Person class as follows:

– (void) setName:(NSString *)aName;

– (NSString*) name;

you declare it using a @property directive:

@property (retain) NSString* name;

And instead of actually implementing the methods, you can use a synthesize directive to ask the compiler to, well, synthesize these functions. So instead of writing something like the following:

– (void) setName:(NSString *)aName

{

    if (name!=aName) {

     [name release];

     name=[aName retain];

    }

}

– (NSString*) name

{

    return(name);

}

you just use:

@synthesize name;

in your implementation file.

 

Now that we finally understand how properties just synthetically replace our good old getter’s and setter’s code, we can go ahead and focus on the latter part of the premise: How do properties help us with our variables’ memory management? Or, now that we know a bit more about properties, we can rephrase that question: How does the compiler know how to implement the getter and setter methods to adhere to standard memory management practices?

 

The answer is quite simple as a matter of fact. The compiler supports a couple of standard memory management paradigms and implements the two methods based on options you supply it in the @property directive, for instance: retain or assign.  The details about the implementation of those options are somewhat explained next. However, if you want to implement the getter and setter in a different way, you can; but this probably means that you’re doing something wrong.

 

We normally deal with one of the two options mentioned before. If we want the class to take ownership of the reference being passed to its property, we use retain. This means that your code should retain new values and release old values when appropriate. When you use @synthesize, it is guaranteed to be so; you do not have to worry exactly how it does that, whether using release or autorelease, whether it checks if the new reference is different than the old one or not, what happens if nil values are introduced, and many other questions that arise when you try to implement getters and setters yourself; instead, you can rest assured that it adheres to owning the current reference and neither over-release nor under-release references.

 

The other option – using assign – is useful whenever you just use a regular assignment and do not take ownership of an object. Of course, this is the case with all C scalars, where no memory management is applicable. But, a common example for using assign when memory management is involved is when you pass a UIViewController reference to your custom UIView objects. Here, of course, you can’t own the controller because the controller owns the view. If the view would own the controller, your classes will never get deallocated. Instead of owning the object when the property is being set, you use the assign option in your property declaration, which references the class, which should be owned somewhere else. You should make sure the ownership holds for as long as you use the variable in your class; of course, this is always the case when dealing with UIViewControllers and UIViews; when it comes to your classes, you need to make sure the same assumption holds.

 

An alert reader will ask: If the @synthesize directive just creates the getter and setter methods, what happens to the references after the class gets released? Are the objects being retained ever released? And the answer to that is that YOU need to include such code into your class’ dealloc method. But instead of meddling with implementation details by releasing the variables, you can just set the property to be nil; this, in turn, calles the setter method, which is guaranteed to work with nils. A normal dealloc method will then look like: 

– (void) dealloc

{

    self.name=nil;

   

    [super dealloc];

}

And this code is valid no matter what kind of option you used for the property’s memory management.

 

Bear in mind that properties are actually using instance variables to store their references. Although other classes interacting with your class must use the property and cannot manipulate your variable directly, you can. When you want to access the object, you don’t need to use the dot notation, and instead can access it directly using the variable, as in:

– (void) internalDebug

{

    NSLog(@"the name is currently: ‘%@’", name);

}

It will be okay for you to use the getter method in your class’s code, although it wastes valuable CPU cycles to just return the variable which you could have accesses directly anyway.

 

Lastly, as a property relies on an instance variable, the class must allocate it first. Apple’s Objective-C documentation states that you should include the variable declaration in your interface code, regardless of the declaration of the property itself, as follows:

@interface Person : NSObject {

    NSString* name;

}

@property (retain, readwrite) NSString* name;

The variable declaration reserves the memory when a class gets alloced, the @property declares the getter and setter methods, and the @synthesize will create the code to support the getter and setter. However, there seems to be another option at first. The alternative is to omit the variable declaration and just allow the @synthesize to modify the class to include the variable automatically. This seems fair and even looks like it’s helping us in hiding another implementation detail. However, this option doesn’t work on 32-bit devices, such as the iPhone simulator on 32-bit Macs; it does work on all iPhones, iPods, iPads, and 64-bit Macs, though. And I have noticed that sub-classing breaks when using @synthesize for this. And, at any rate, Apple doesn’t specifically recommend this; so unless you like rewriting your own code, you should avoid it. 

 

Properties support many other options, like choosing different getter and setter method names, declaring a read-only property, and so on and so forth, but the options are simple enough for you to pick up from the Apple docs. To wrap things up, I am listing the entire source code exemplifying the use of properties. Enjoy.

 

//

//  Locale.h

//  TestApp

//

//  Created by Ohad Kravchick on 11/1/10.

//  Copyright 2010. All rights reserved.

//

 

#import "Person.h"

@interface Locale : NSObject {

    Person* celeb;

}

– (id) init;

– (void) showData;

@end

 

//

//  Locale.m

//  TestApp

//

//  Created by Ohad Kravchick on 11/1/10.

//  Copyright 2010. All rights reserved.

//

 

#import "Locale.h"

 

@implementation Locale

 

– (id) init

{

    if (self=[super init]) {

     celeb=[[Person alloc] init];

     celeb.name=[NSString stringWithFormat:@"%@ the %@", @"Eric", @"2nd"];

    }

    return(self);

}

 

– (void) showData

{

    NSLog(@"Our local celebrity is: %@", celeb.name);

}

@end

 

 

//

//  Person.h

//  TestApp

//

//  Created by Ohad Kravchick on 11/1/10.

//  Copyright 2010. All rights reserved.

//

 

#import <Foundation/Foundation.h>

 

@interface Person : NSObject {

    NSString* name;

}

 

@property (retain, readwrite) NSString* name;

 

@end

 

//

//  Person.m

//  TestApp

//

//  Created by Ohad Kravchick on 11/1/10.

//  Copyright 2010. All rights reserved.

//

 

#import "Person.h"

 

@implementation Person

 

@synthesize name;

 

– (id) init

{

    if (self=[super init]) {

     self.name=@"Eric";

    }

    return(self);

}

 

– (void) internalDebug

{

    NSLog(@"the name is currently: ‘%@’", name);

}

 

– (void) dealloc

{

    self.name=nil;

   

    [super dealloc];

}

 

@end

 

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: