Skip to content

Instantly share code, notes, and snippets.

@mattmassicotte
Created July 20, 2012 13:11
Show Gist options
  • Save mattmassicotte/3150651 to your computer and use it in GitHub Desktop.
Save mattmassicotte/3150651 to your computer and use it in GitHub Desktop.
Dot-syntax assignment versus traditional assignment
// clang -framework Foundation -c properties.m
#import <Foundation/Foundation.h>
@interface PropertyBug : NSObject {
NSString* _value;
}
@property (copy) NSString* value;
@end
@implementation PropertyBug
@synthesize value = _value;
@end
int main(const int argc, const char** argv) {
PropertyBug* object;
object = [PropertyBug new];
// this should be an error...
if (object.value = nil) {
NSLog(@"dot-sytnax");
}
// ... if it really is equivalent to this
if ([object setValue:nil]) {
NSLog(@"traditional");
}
return 0;
}
@mattmassicotte
Copy link
Author

Perhaps even more fascinating is that "if (object.value = nil)" evaluates to false, and "if (object.value = @"abc")" evaluates to true, even if object.value is hard-coded to always return nil. This construct is really bizarre, and appear to disagree with traditional C semantics.

@numist
Copy link

numist commented Sep 18, 2012

That actually makes convoluted sense. Mutating a structure via dot/arrow notation linguistically (according to traditional C semantics) implies directly setting the lval as opposed to calling a method, and returns the value that was set (which is the lval or the rval—they are equivalent because the assignment operator doesn't fail).

Setter methods are expected to return void (and again, the assignment operator in C does not fail), so the runtime makes the (relatively safe) assumption that the object's setter also doesn't fail and wraps the call to return the boolean value of the argument to the setter, thus providing a condition as expected by C assignment semantics.

The inconsistency is that the runtime should be using object.value as the value to cast (in case of a setter failure), not the argument to the setter. I'd say it's a runtime bug, but it's probably intentional since your getter could be custom (and also result in odd side effects—you have a failing setter so your getter is probably too clever for its own good, too), so using the rval is the safest approach since you can easily capture its pointer (or value, if a POD type) without causing any side effects.

Really, the problem is that a runtime engineer was clever :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment