Saturday, September 26, 2009

Read for You, Write for Me, Right?

Properties are a way to expose data to other classes. Because they make memory management more convenient on the iPhone and in non-GCC Cocoa apps when you declare them retain, many of us also use properties within our class. When we use synthesized instance variables, we don't have a choice, because the underlying instance variables created by the runtime aren't available even to our own class, and we have to use the accessors and mutator or dot notation to use them.

What happens, however, when you want a property to be read-only to other objects, but you also want to be able to use the synthesized mutator within your own class to assign new values? In other words, what do you do when you want a property to be read-only to the rest of the world, but read/write within your own class?

Extensions to the rescue. Before we had extensions, it was common to declare a category at the top of your implementation file with any private methods. This would prevent the compiler from complaining when you called a private method from within your class. The compiler would see the private method declaration in the implementation file when your class was compiled, but wouldn't see those methods when it compiled other classes. Under this old way of doing it, we would simply include the mutator method in the category, and only put the accessor in the header file, and that would effectively make the instance value read-only to the rest of the world by not exposing the mutator.

In Objective-C 2.0, this practice has been formalized into extensions, which are basically just anonymous, nameless categories. One thing you can do with an extensions is to redefine your declared properties. So, you can declare a property in your header file as readonly, and then in your implementation file, create a class extension with the same properties re-declared as readwrite. Ét voilà! Your class will be able to use both the accessor and mutator for those properties while external classes will only be able to use the accessor. Cake and nom nom. Life is good.

Note: you cannot change all aspects of a property. You can't, for example, declare it copy in your header file and retain in your extension.
Let's look at a simple example. Let's say we were writing a subclass of NSOperation that had two properties. We want these values to be set by in the init call, but because our operation will be running concurrently on another thread, we don't want the values changed after that by external objects, but we may need to change them ourselves. To do that, we might declare our class like this:
#import <Foundation/Foundation.h>
#import <QTKit/QTKit.h>


@interface MyUpdateOperation : NSOperation {

}

@property (readonly, copy) QTMovie *movie;
@property (readonly, copy) NSView *view;
- (id)initWithMovie:(QTMovie *)inMovie andView:(NSView *)inView;
@end


And then, in the implementation file, we would use an extension to redefine those two properties:

#import "MyUpdateOperation.h"


@interface MyUpdateOperation ()
@property (readwrite, copy) QTMovie *movie;
@property (readwrite, copy) NSView *view;
@end


@implementation MyUpdateOperation
- (id)initWithMovie:(QTMovie *)inMovie andView:(NSView *)inView
{
// Logic goes here
}

-(void)main
{
// Logic goes here
}

@end


It's a simple trick, but one with the potential to make your life more pleasant.

0 nhận xét:

Post a Comment

 
Design by Wordpress Theme | Bloggerized by Free Blogger Templates | coupon codes