Properties in Objective-C 2.0

Properties in Objective-C 2.0

Posted 10/29/2008 - 00:34 by cocoacast

Properties have only appeared in Objective-C recently, with the 2.0 release. They are kind of virtual member variables. If you think about it, there are only two things you can do to a variable: assign a value and extract a value. You can make variable a constant to prevent it from being set, or hide it by making it private or protected, but in no way does setting a member variable’s value affect the behavior of an object. Properties do not have to do that but they can. In Object Oriented Languages, member variables describe the state of an object, while functions (methods, messages) describe its behavior. Properties allow us to morph together the state and the behavior.

Property as a conveyor of State

Consider the following code:

@interface Test0 : NSObject {
        @private int iVar;
}
@property (readwrite,assign) int iVar;
@end

...
@implementation Test0
@synthesize iVar;
@end

...
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    // insert code here...
    Test0 *test0 = [[Test0 new] autorelease];
    test0.iVar = 5;
    NSLog(@"%d", test0.iVar);
    [pool drain];
    return 0;
}

After you declare a property in Objective-C, you have to tell the compiler to instantiate it by using @synthesize directive. This actually tells the compiler to generate a getter and a setter messages. By default, these messages are named var and setVar, where var is a name of the variable. Also, by default, the variable represented by the property has the same name as a property. In our example, the compiler will generate the following:

-(void)setIVar:(int)i
{
        iVar = i;
}
-(int)iVar
{
        return self->iVar;
}

As you can see from the Test0 example, properties can be accessed by using the DOTted notation. They can also be accessed via accessor methods, for example [self setIVar:5] or int i = [self iVar].
We can add simple permissioning by specifying readonly attribute instead of readwrite.

@property (readonly,assign) int iVar;

This would prevent the property from being assigned to. You can still change the value of the corresponding member variable, though, by referencing it directly. For example, I can add a message foo:

-(void)foo
{
    self->iVar = 5; //legal because we are referencing a member variable
    iVar = r; // illegal because we are referencing a readonly property
}

We can even do this:
@interface Test1 : NSObject {
        @public int iVar1;
}
@property (readwrite,assign) int iVar;
@end

...
@implementation Test1
@synthesize iVar=iVar1;
@end

...
Test1 *test1 = [[Test1 new] autorelease];
test1.iVar = 6;
NSLog(@"%d", test1->iVar1);    

Here, we are actually telling the property to latch to different a different instance variable.

I showed all these examples is to show you how to use the properties to describe the state of an object.

Property as a conveyor of the Behavior

As I mentioned previously, compiler generates accessors for each property. Read-write properties get both a getter and a setter, while read-only properties get only a getter. Getters and setters are messages, and if overwritten, can be used to do virtually anything.

@interface Test2 : NSObject {
        @private int iVar1;
}
@property (readwrite,assign) int iVar;

-(void)foo;
@end

@implementation Test2
@synthesize iVar=iVar1;

-(void)foo
{
        NSLog(@”Foo called”);
}

-(void)setIVar:(int)inIVar
{
        iVar1 = inIVar;
        [self foo];
}
@end

When we defined a setter, we told the compiler that it does not need to generate another one. Notice how we called [self foo]. We just altered the behavior of the object.
Another way to achieve similar results is by using @dynamic directive. @dynamic tells the compiler not to generate the accessor messages and that we will do it ourselves either by directly defining them or through introspection.

@interface Test3 : NSObject {
}
@property (readwrite,assign) int iVar;
@end
@implementation Test3
@dynamic iVar;
-(int)iVar
{
        NSLog(@”iVar called”);
        return 1;
}
-(void)setIVar:(int)inIVar
{
        NSLog(@”setIVar called”);
}
@end

Here, we actually went a step further. As you can see, we don’t even have to alter the state. We can simply alter the behavior of the object!
In reality, accessors don’t even need to be named in default fashion. We can provide our own names by using getter and setter attributes.

@interface Test4 : NSObject {
        @private int iVar;
}
@property (getter=foo,setter=foo1:) int iVar;
@end
@implementation Test4
@synthesize iVar;
-(int)foo
{
        return 4;
}
-(void)foo1:(int)inFoo
{
        self->iVar = inFoo;
}
@end

More Attributes

We already saw a few attributes, such as readwrite and readonly, getter and setter. Here are some other attributes:

assign, retain, copy

These attributes are pretty self explanatory. They have to do with the way instance variables are assigned. They are also mutually exclusive. assign enforces a brute-force assignment of a value, retain retains a pointer, and copy assigns a copy of the original.
Imagine this:

@interface Test5 : NSObject {
        @private NSString *sVar;
}
@property (readwrite, assign) NSString *sVar;
@end

The setter method generated will look like this:

-(void)setSVar:(NSString*)inSVar
{
        if (self->sVar != inSVar)
        {
                [self->sVar release];
                self->sVar = [inSVar retain];
        }
}

What if we decided to assign-by-copy:

@property (readwrite, copy) NSString *sVar;

...
-(void)setSVar:(NSString*)inSVar
{
        if (self->sVar != inSVar)
        {
                [self->sVar release];
                self->sVar = [inSVar copy];
        }
}

When would we want to do copy assignment? When there is a possibility that inSVar is mutable. Since NSMutableString derives from NSString, we may have a problem if the behavior of the object depends on immutability of sVar. You can find out more about this from "Advanced Strings in Cocoa" article.

nonatomic

By default, property assignments are atomic, i.e. they are mutexed to guarantee thread-safety. Atomicity is a nice feature, but hardly ever needed if application is not multithreaded or you guarantee thread-safety on your own. Therefore, it’s a good practice to use nonatomic attribute, while providing own thread-safety.

@property (readwrite, nonatomic) NSString *sVar;

A Trick

There is an interesting side-effect to assigning nil to a pointer property. What happens when I execute the following code?

@property (readwrite, nonatomic, retain) NSString *sVar;

...
-(void)foo
{
        self.iVar = @”123”;
        self.iVar = nil;

        [iVar release];
}

The side-effect of this code is that iVar is not only properly released, but the iVar now points to a nil value. Therefore, [iVar release] WILL NOT crash the code.

Conclusion

The purpose of this article was to give a general introduction to properties. There is more to learn about properties that I was able to discuss. For more details, please refer to “The Objective-C 2.0 Programming Language” guide.
Code for this article can be found here.

10/17/2008
Vladimir Pasman

Trackback URL for this post: http://www.cocoacast.com/cocoacast/?q=trackback/103
3.666665
Your rating: None Average: 3.7 (3 votes)
I think your last comment is

I think your last comment is the right one, getters might return an autoreleased object. I unfortunately couldn't find any document from Apple clearly stating it... Eddy, my mini store, apple laptops for sale.

Posted by Eddy on Wed, 03/10/2010 - 06:41
There is need of developing

There is need of developing more standards in this regard, as we all know that this is a great work. So what we need is to avoid the mistakes and remove the flaws. Thanks Dermajuv

Posted by gittar1122 on Wed, 03/10/2010 - 08:38
View the Podcasts

I strongly recommend that if you really want to get to grips with 'properties' and other aspects of this mighty app. then view the podcasts. Much easier to follow than text - IMHO:
Birmingham Airport Parking

Posted by syndikate on Mon, 03/08/2010 - 10:36
Is it correct?

In your post yo say this:

*********************
Imagine this:

@interface Test5 : NSObject {
@private NSString *sVar;
}
@property (readwrite, assign) NSString *sVar;
@end

The setter method generated will look like this:

-(void)setSVar:(NSString*)inSVar
{
if (self->sVar != inSVar)
{
[self->sVar release];
self->sVar = [inSVar retain];
}
}
*********************

Is that correct? it seems to me more like a "retain" than an "assign".

Bye,
Daniel W.

Posted by dwilches on Sat, 03/06/2010 - 14:35
could it be a multi-threading issue

Would I be correct in assuming that you do not provide own getters/setters for your property. Another possibility that comes to mind is that you somehow have a multi-threaded application and possibly referencing the same property from multiple threads. In that scenario, a setter on another thread can alter the retain count just before you check it after the getter call. It would be hard to help you without seeing the code for the scenario you are using, so feel free to send it to cocoacast@gmail.com

Posted by vlad on Thu, 11/20/2008 - 12:58
What about getters?

Does getters increase the retaincount?

@property (readwrite, retain) NSString *name;

Everytime I call this getter the retain count increases. I thought retain/copy was related to the setter only.

Does this mean I will have to release/autorelease everytime i call this method. Even though I didn't write this code (or known what is happening) I get the feeling I am responsible for releasing this returned object. Or is the getter code placing an autorelease on the returned object?

Posted by tankit on Mon, 11/17/2008 - 18:35
Re: What about getters?

I think your last comment is the right one, getters might return an autoreleased object. I unfortunately couldn't find any document from Apple clearly stating it...

~Philippe

Posted by philippe on Tue, 11/18/2008 - 12:56
Good point. Getters can

Good point. Getters can return an auto-released object. Not sure why they would want to do it though. If the data is held by the object you are asking for getter, that object is already retained (or at least it should be), but it does not have to be this way, so yes it could be auto-released objects that is coming back.

Thanks Philippe for pointing that out... I believe Vlad would be the best person to put the final thought on this subject.

~Boris

Posted by cocoacast on Tue, 11/18/2008 - 13:40
Getters and retain count

Getters by definition just a way to access an instance variable an as such should not ever retain anything. That said, if you write some kind of custom getter that would for example track how many times someone asked for the variable (or even get crazy and store the sender as last caller), then the answer is maybe depending on what you would have in the getter.

In your example, you are having both getter and setter synthesized for you (readwrite tells the compiler to do this). Can you post your @synthesize directive and context where you make the call so we could help you better understand the issue?

Great question, we should have explained it better I guess. It just felt so obvious to us.

Posted by cocoacast on Mon, 11/17/2008 - 22:54
Good article, but...

I can't fault the article's contents - it's very good. But there are couple of typos and formating errors.

In the paragraph explaining the code in the "Property as a conveyor of State" section it says @synchronize when it should say @synthesize.

It also looks like font setting for "A Trick" code sample did not end when it should have. The last 2 secences of the article have the same format as the code sample.

Posted by dgethings on Wed, 10/29/2008 - 02:37
Good article, but...

Thanks a lot for your comments! I think I just fixed those formatting issues.

Posted by cocoacast on Wed, 10/29/2008 - 08:35