Saturday, June 19, 2010

Properties in Objective-C 2.0 .They are kind of virtual member variables.

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 varis 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.

No comments:

Post a Comment