Beware of struct properties

January 1, 2009

Objective-C is an interesting programming language. Unlike C++, it’s a strict superset of C — every valid C program is a valid Objective-C program. While most C programs are valid C++ programs, there are certain incompatibilities such as the introduction of new keywords that make the relationship not strict.

One of the many features that Objective-C brings is properties. Properties essentially act like public member variables, but whenever you get or set them, those gets and sets get rerouted through a function call (more specifically, the passing of a message in Objective-C parlance). For example, if value is a property of whatever class my_object belongs to, then the code

int x = my_object.value;

gets converted into

int x = [my_object value];  // pass the 'value' message to my_object

And this code

my_object.value = 3;

gets converted into

// pass the 'setValue' message with parameter 3 to my_object
[my_object setValue:3];

Getters and setters allow you to do things like parameter validation, logging, or anything else you want whenever a property is read from or written to. The dot notation is just syntactic sugar.

Now suppose that value isn’t a simple integer, but rather a struct of some sort, such as a CGPoint. A CGPoint is a standard 2D point class, containing two float members named x and y. Consider the following innocent-looking code:

my_object.value.x = 3;  // set x-coordinate to 3?

If value were just a plain ordinary struct member, this would do exactly what you’d expect – set the x-coordinate to 3, and leave the y-coordinate untouched. But as you’ve probably surmised by now, that’s not what happens when value is a property. Why not? Well, since we’re not assigning to it, it gets turned into this:

[my_object value].x = 3;

And [my_object value] returns an object of type CGPoint. And structs are returned…..by value. Thus, we’re assigning 3 to the x-coordinate of a temporary CGPoint which is a copy of our object’s value property. The only proper way to resolve this is to make value a regular class member and not a property, reassign a completely new CGPoint to value, or add messages that allow you to independently set the x- and y-coordinates. Depending on whether or not you own the code for the class, this may or not be possible.

Fortunately, in the case of CGPoints, it’s relatively easy to assign a new value with CGPointMake():

my_object.value = CGPointMake(3, my_object.value.y);

But for more complicated structs, this solution could be less than ideal.

Comments are closed.