fbpx

Blogs from the Ranch

< Back to Our Blog

Read/Write all about it!

Avatar

Mark Dalrymple

This question came up in an IRC channel the other day: “What’s the best way to set up a property that’s read-only externally, but modifiable inside of the class, so I can use properties or KVC to change it?”

TL;DR: Make readonly in a public @property, make readwrite in a class extension.

The Details

Properties by default are read/write. This property:

@property (assign) NSInteger frobulationThreshold;

States that (semantically) frobulationThreshold is a mutable property. You can query it. You can change it, with reasonable expectation that you’ll get the same value back next time you query it unless somebody else changes it.

What’s happening under the hood is the compiler is acting as if it had seen these declarations:

- (NSInteger) frobulationThreshold;
- (void) setFrobulationThreshold: (NSInteger) frobThresh;

You can declare a property to be read-only, say to get the number of elements in a collection.

@property (readonly) NSUInteger count;

All this does is have the compiler fantasize that it has seen this declaration:

- (NSUInteger) count;

Outside of that, nothing happens.

Synthesizing

Things get more interesting when you @synthesize one of these properties. The @property just tells the compiler to pretend that it saw method declarations of a certain form. @synthesize (whether explicitly written, or implicitly performed with newer Xcodes) takes the various attributes of the @property declaration, and emits object code for any of the accessors that don’t otherwise exist. Given the frobulationThreshold property declaration above, if there were no explicitly implemented setters or getters, the compiler would emit an implementation that used atomic access (the default), to retrieve and change an instance variable. Given the count @property, the compiler would emit a similar single method’s implementation.

Read-only / Read-write

So now the actual answer to the question. Say you wanted to make frobulationThreshold a read-only property that was modifiable inside of the implementation.

In the header file, you’ll want a property that’ll make the compiler think it’s only seen

- (NSInteger) frobulationThreshold;

And not the setter:

@property (readonly) NSInteger frobulationThreshold;



Is sufficient. Because of the readonly, only -frobulationThreshold is considered to exist, but not -setFrobulationThreshold: Now everyone who #imports this header will see the readonly property, and if they try to change it, the compiler will complain with a “I have not seen a declaration for a method called -setFrobulationThreshold:. I shall now warn in your general direction.” (And you are on top of fixing your Warnings right?)

Inside of the implementation file, though, you can create a class extension that redeclares the property as readwrite:

@interface Frobinizer ()
@property (assign, readwrite)  NSInteger frobulationThreshold;
@end // extension

The readwrite is actually optional in this case, but I like adding it to make it explicit that this is a property declaration that exists to turn a read-only property into a read/write one

When the compiler sees this, it adjusts its concept of method declarations it thinks it has seen. Before processing the @property above, the compiler thought it only saw -frobulationThreshold thanks to the readonly property in the header file. Now with this property redeclaration, the compiler thinks it has seen now seen both -frobulationThreshold and -setFrobulationThreshold:. Et voila, the property is now read/write, as far as any code after this class extension is concerned. Code can happily call self.frobulationThreshold = 23;, or [self setFrobulationThreshold: 23]; to change that property.

Is it really read-only in the public API?

Of course not, this is Objective-C. Because there will exist a setFrobulationThreshold: (either explicitly written, or synthesized) as a method of the class, anyone who is aware of it can call it. That’s just the nature of Objective-C. There are no private methods.

Using read-only properties does help “keep honest people honest”. You have to do some work to call setFrobulationThreshold from outside of its implementation file, and if you are jumping through those hoops, you should feel dirty enough to reconsider what you’re doing, or to lobby to have the property changed to be publicly read/write.

Avatar

Mark Dalrymple

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project