Objective-C struct property pointer cannot be made via self.prop?
-
28-12-2020 - |
Pregunta
I've just encountered a little surprise whilst writing NSCoder support into a class. I have a class A which contains an exposed 'color' property of an rgb struct type. I have another class B which extends A.
To encode the color property within class B's encodeWithCoder method I've first wrapped it into an NSValue like so:
[NSValue value:&(self.color)]
However using this code causes the following compilation error:
Lvalue required as unary '&' operand.
It's easy enough to get around by simply copying self.color to a method-scope value before wrapping with NSValue, but I'd like to understand the rule for when this will happen, and why it occurs. Will this happen with any @synthesize property due to there being no need for a true class field of that property name to exist, hence no guaranteed object to point to?
Solución
The rule is simple enough. The compiler turns a property access:
self.color
into a method call*
[self color]
which means that &(self.color)
turns into &([self color])
. Then the compiler complains, because you can't take the address of a method call.
*If you were assigning to the property, the assignment would be transformed into a call to the setter: self.color = aColor;
-> [self setColor:aColor];
.
Otros consejos
You cannot get the address of something returned by a function (or method). You need to get the address of the color
ivar directly (i.e. &color
). In your case though, I recommend using the approach you proposed yourself (id tempCol = self.color; [NSValue value:&tempCol];
).
Note that a method is invoked when using dot notation.
self.color
// is equal to
[self color]
&([self color])
is also impossible because &
is resolved at compile-time, not at runtime. The return value of [self color]
and self.color
(which are equal) aren't yet known at compile-time (even when using @synthesize
, since you can change a method's implementation in Objective-C at runtime (e.g. with method swizzling or using categories)).
It seems that you don't fully understand what @synthesize
does, so I'll explain. :)
Basically what @synthesize
does is this: the compiler generates methods and their implementations from the given property names.
For example, this property:
@property(nonatomic, retain) NSColor *color;
Together with this synthesization:
@synthesize color;
Will insert this code for you in the @implementation
:
- (NSColor *)color {
return color;
}
- (void)setColor:(NSColor *)newColor {
if (color == newColor) { return; }
NSColor *theOldColor = color;
color = [newColor retain];
[theOldColor release];
}
When you like to use your own setter or getter, you simply define these in the @implementation
by using the same method names. You could, for example, return a default color if none is set yet:
- (NSColor *)color {
if (color) { return color; }
return [NSColor whiteColor];
}
Now the compiler will generate the setter, but not the getter: it will use the getter you provided, you can still use it with self.color
.
You may want to take a look at Apple's guide on properties.