What does Objective-C property get resolved to in runtime? Will calling [obj valueForKey:#"property"] always yield the same result?
e.g.
obj.property
First, note that obj.property is precisely the same as [obj property]. Dot syntax is just syntactic sugar. While there are some small run-time implementation details related to properties that are different than other methods, for the purposes of this discussion, think only in terms of "I have an ivar named _key and a method called -key." The fact that you created that ivar and method by declaring a property is irrelevant for valueForKey:.
valueForKey: is a method, and it can be overridden by a class to return whatever it likes. The default behavior is that valueForKey: will first look for a method whose name matches the key, and will return the result of that. In the vast majority of cases, this means that if you have a property, then valueForKey:#"property" will return the value of it.
The full search path for the default implementation of valueForKey: is explained in "Accessor Search Implementation Details", but here is the "short" version:
get<Key>, <key>, is<Key> (yes, the first place it looks is getKey, which is a little embarrassing because you should not prefix getters with get unless they return values by reference, but there you go; it is the first thing checked.)
countOf<Key>, objectIn<Key>AtIndex:, and <key>AtIndexes. If a legal combination of these are found, then an NSArray-like proxy object is returned.
countOf<Key>, enumeratorOf<Key>, and memberOf<Key>:. If all three are found, then an NSSet-like proxy object is returned.
If accessInstanceVariablesDirectly is YES (the default), then ivars are checked, named _<key>, _is<Key>, <key>, or is<Key>. Yes, this is a way to access an object's private ivars.
If everything else failed, then it calls valueForUndefinedKey:, which is free to return a result (and this is in fact a very useful thing to do if you want a generic key/value store).
But nine times out of ten, you're going to get the value of the method named <key>.
Side note: valueForKey: returns an object. If the return is a number-like scalar (including BOOL), it will return an NSNumber. Otherwise it will return an NSValue. There is some special handling for NSPoint, NSRange, NSRect, and NSSize (on Mac; on iOS, only NSRange is handled specially I believe).
obj.property is the same as [obj property], not [obj valueForKey:#"property"];. The latter is part of a system called Key Value Coding that's separate from properties.
Related
It seems that -valueForKey: and -valueForKeyPath: work with arbitrary methods, not only with properties. This seems very convenient:
I first stumbled upon it in Interface Builder, and then made some experiments:
// Thing.h
#import <Foundation/Foundation.h>
#interface Thing : NSObject
- (BOOL) alwaysYES;
- (BOOL) alwaysNO;
#end
// Thing.m
#import "Thing.h"
#implementation Thing
- (BOOL) alwaysYES
{
return YES;
}
- (BOOL) alwaysNO
{
return NO;
}
#end
I can call these methods via -valueForKey: and -valueForKeyPath: despite the fact that they are normal methods and no properties:
Thing *aThing = [[Thing alloc] init];
id result;
result = [aThing valueForKey:#"alwaysYES"];
NSLog(#"result is: %#", result);
result = [aThing valueForKeyPath:#"alwaysNO"];
NSLog(#"result is: %#", result);
Compiles, runs and gives the correct results. Is this documented anywhere? Can I safely use it? How can i understand it?
Cocoa's key-value coding (KVC) system is older than support for explicit properties (declared with #property) in Objective-C, so KVC is defined in terms of methods, not properties.
“Default Search Pattern for valueForKey:” in the Key-Value Coding Programming Guide spells out how valueForKey: decides what to do. It starts by looking for (amongst other things) a method whose name is exactly the key you passed to valueForKey:. Here is the full search pattern, quoted from the documentation:
Searches the class of the receiver for an accessor method whose name matches the pattern get<Key>, <key>, or is<Key>, in that order. If such a method is found it is invoked. If the type of the method's result is an object pointer type the result is simply returned. If the type of the result is one of the scalar types supported by NSNumber conversion is done and an NSNumber is returned. Otherwise, conversion is done and an NSValue is returned. Results of arbitrary types are converted to NSValue objects, not just NSPoint, NSRange, NSRect, and NSSize types).
Otherwise (no simple accessor method is found), searches the class of the receiver for methods whose names match the patterns countOf<Key> and objectIn<Key>AtIndex: (corresponding to the primitive methods defined by the NSArray class) and <key>AtIndexes: (corresponding to the NSArray method objectsAtIndexes:).
If the countOf<Key> method and at least one of the other two possible methods are found, a collection proxy object that responds to all NSArray methods is returned. Each NSArray message sent to the collection proxy object will result in some combination of countOf<Key>, objectIn<Key>AtIndex:, and <key>AtIndexes: messages being sent to the original receiver of valueForKey:. If the class of the receiver also implements an optional method whose name matches the pattern get<Key>:range: that method will be used when appropriate for best performance.
Otherwise (no simple accessor method or set of array access methods is found), searches the class of the receiver for a threesome of methods whose names match the patterns countOf<Key>, enumeratorOf<Key>, and memberOf<Key>: (corresponding to the primitive methods defined by the NSSet class).
If all three methods are found, a collection proxy object that responds to all NSSet methods is returned. Each NSSet message sent to the collection proxy object will result in some combination of countOf<Key>, enumeratorOf<Key>, and memberOf<Key>: messages being sent to the original receiver of valueForKey:.
Otherwise (no simple accessor method or set of collection access methods is found), if the receiver's class method accessInstanceVariablesDirectly returns YES, the class of the receiver is searched for an instance variable whose name matches the pattern _<key>, _is<Key>, <key>, or is<Key>, in that order. If such an instance variable is found, the value of the instance variable in the receiver is returned. If the type of the result is one of the scalar types supported by NSNumber conversion is done and an NSNumber is returned. Otherwise, conversion is done and an NSValue is returned. Results of arbitrary types are converted to NSValue objects, not just NSPoint, NSRange, NSRect, and NSSize types.
If none of the above situations occurs, returns a result the default implementation invokes valueForUndefinedKey:.
This is parallel to the fact that you can call these methods using property syntax:
BOOL ok = aThing.alwaysYES
In that case and in your case, exactly the same thing happens: the first thing the runtime tries is to treat this as a getter method. What you've written is a getter method.
As for your question "can I safely use it", safely yes, but what you're doing is kind of silly, since you know (and have declared) that these methods exist. KVC is about probing to see whether methods exist. If you have a reason to specify one of these methods by string name, there are better ways to call it than using KVC.
Properties are nothing special at runtime; they generate a getter and setter (if not readonly) which conforms to KVC; for example:
#property NSString *aString;
will generate:
- (NSString)aString {
...
}
- (void)setAString(NSString *string) {
...
}
just as if you had declared those methods in the header (which itself is optional).
See the Key Value Coding Fundamentals documentation.
So, I've already read up on the documentation which notes
Objective-C 2.0’s dot syntax and key-value coding are orthogonal technologies. You can use key-value coding whether or not you use the dot syntax, and you can use the dot syntax whether or not you use KVC. Both, though, make use of a “dot syntax.” In the case of key-value coding, the syntax is used to delimit elements in a key path. It is important to remember that when you access a property using the dot syntax, you invoke the receiver’s standard accessor methods.
It then provided an example that supposedly showed the difference between the two. However, I still don't get, what's the difference between KVC and property accessor methods? Aren't they the same? And how do I distinguish between dots that call setValue:forKeyPath: and simple accessors?
However, I still don't get, what's the difference between KVC and property accessor methods?
KVC is a way to call property accessor methods, or otherwise access a property.
What do I mean by “otherwise access”? For KVC purposes, an instance variable with no accessor methods counts as an informal property. It'll get or set the value of the instance variable directly if no matching accessor pair can be found. (Yes, this is not worth using in modern code. Always declare an #property for anything you intend to access elsewhere, and, inversely, don't use KVC to access anything that isn't a public property.)
Property accessor methods are what KVC will call if they exist (preferred, both by KVC and by every sane programmer, over direct ivar access). An accessor may get or set an instance variable, as synthesized accessors do, or access some other storage.
Accessors are implementation, properties are interface, and KVC is one way to use them.
And how do I distinguish between dots that call setValue:forKeyPath: and simple accessors?
A key path is a string, whereas a property-access expression is an expression. The compiler evaluates a property-access expression and translates it into one or more Objective-C messages, whereas a key path is evaluated by KVC at run time.
So, when you use a key path:
[someObject setValue:theValue forKeyPath:#"foo.bar.baz"];
You know it's a key path because (1) it's a string, as indicated in this case by the string-literal syntax #"…", and (2) you're passing the key-path string to setValue:forKeyPath: for it to evaluate.
Using a key path is using KVC to access the named properties. It will send any relevant accessor messages on your behalf.
When you use a property-access expression:
someObject.foo.bar.baz = theValue;
You know it's a property access expression because you are not identifying the properties with a string. You are accessing them (sending the accessor messages) yourself, in your own code.
There isn't much reason to use KVC in any form; when you know the property at authorship/compile time, it's best to have an #property declared and to access the property yourself, whether with property-access expressions or message expressions ([[[someObject foo] bar] setBaz:theValue]). The time to use KVC is when you don't know what property you want to access until run time, which is pretty rare. It's mainly a building-block technology behind KVO, Cocoa Bindings, parts of Core Animation, etc.
Mostly, you'll only want to access properties yourself.
Key value coding allows you to set and get the value of properties through code using the string name of the property. For example, if I had a property named foo which is of type NSString:
[self setValue:#"mystring" forKey:#"foo"];
// read the value by key
NSString *s = [self valueForKey:#"foo"];
Dot syntax is compile syntax sugar. As a personal preference (as some don't agree - fine) I don't use dot syntax but I still use KVC:
[myObj setFoo: #"someString"]
equals:
myObj.foo = #"someString";
They are orthogonal, different concepts but both dealing with how you interact with properties
Finally, you mention property syntax. Yet another orthogonal concept but related to dealing with properties.
With objective-c, convention is important. Follow them. Properties are the name of the property for the get and set[Name] for the assignment:
- (NSString*)foo
{
return _foo; // defined as (NSString*)_foo in header
}
- (void) setFoo: (NSString*)foo
{
if (foo == _foo)
return;
NSString* curr = _foo;
_foo = [foo retain];
[curr release];
}
Now, who wants to write something like that every time. So, enter #property syntax:
In header:
#property (retain) NSString *foo;
Then in .m:
#synthesize foo;
That's the equivalent of the hand written property accessors. It's compiler syntax sugar which expands the property code based on how you attribute the properties.
Docs:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.html
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html
For the Objective-C gurus:
Suppose I have a simple method like so:
-(id)getValue{ return [NSNumber numberWithDouble:5.0]; }
Now, suppose within some other method I call the (id)getValue method like so:
NSNumber* myValue = [self getValue];
or what if I call it like this instead:
NSNumber* myValue = (NSNumber*)[self getValue];
The question is: Obviously these lines are equivalent but one of them utilizes an explicit cast. So what is the correct or best-practice way of doing this. It seams to me the cast is unnecessary since when it is placed in the pointer myValue, it will be type-safe at this point anyways (which is something I want) so the cast is basically pointless.
Let me just add that I'm sure people will point out: Why don't you just return (NSNumber*) from the getValue method but in my case I want to have the flexibility to return whatever I want much like the built in NSDictionary class returns id when you call: objectForKey because it allows you to place any type of NSObject or subclass inside of it. In other words my getValue method will not always be returning an NSNumber. Also consider this example is contrived because I am just concerned about whether to cast or not.
Thank you in advance,
-Ralph
The only reason to cast objects is to make the compiler happy. (Sometimes it also helps readability.) For example, you have to cast when making a property access directly on an object you're getting out of an array or dictionary:
((Foo *)[myArray objectAtIndex:0]).bar;
If you don't do the cast, the compiler can't do the property lookup, and will complain.
When you're getting an object from a method that returns id, it's impossible for the compiler to know what its actual type is. There isn't really any "type-safety", because id is a generic pointer; all the compiler can and will enforce is that the method says it returns some Objective-C object. It is perfectly happy to assign a generic pointer to any typed pointer.* (This is actually an advantage for containers, obviously.) Since the type of the variable to which you're assigning already documents the actual return type, I'd say there's no need for the cast.
As an aside, you shouldn't be calling your method getX. That has a specific meaning in Cocoa; methods which "get" something pass in a pointer to a pointer, which is then filled by the method. See -[NSArray getObjects:range:] as an example.
*The type will be enforced at run-time, of course, in the sense that sending messages to which the object does not respond will cause an error.
In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.
For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:
Not all properties of the existingObj are present in the updateDict
Not all properties of the updateDict are available in the extistingObj.
Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
updateDict may contain values for keys that are uninitialized (nil) in existingObj.
This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:
// key is an NSString, e.g. #"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
[existingObj setValue:[updateDict objectForKey:key] forKey:key];
}
Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.
This makes for subquestion A: How does one test for a property setter, if you only have the property's name?
The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key #"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key #"color" that is #"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:
if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
// regular assignment
} else {
// perform custom assignment
}
Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.
Subquestion B: How does one test for the type of a property, if you only have the property's name?
I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.
P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.
If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,
NSString *key = #"displayName";
NSString *setterStr = [NSString stringWithFormat:#"set%#%#:",
[[key substringToIndex:1] capitalizedString],
[key substringFromIndex:1]];
if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
NSLog(#"found the setter!");
[obj setValue:someValue forKey:key];
}
Two remarks:
Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.
KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.
As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely #) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.
If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.
I have a database model class that is a NSObject. I have a set of these objects in a NSMutableArray. I use indexOfObject: to find a match. Problem is the model object's memory address changes. So I am overriding the hash method to return the model's row ID. This however does not fix it. I also have to override the isEqual: method to compare the value of the hash method.
What does the isEqual: method use to determine equality by default?
I'm assuming it uses the memory address. After reading the isEqual: documentation I thought it used the value from the hash method. Obviously, that is not the case as my attempt to override that value did not solve my initial problem.
As you've correctly guessed, NSObject's default isEqual: behaviour is comparing the memory address of the object. Strangely, this is not presently documented in the NSObject Class Reference, but it is documented in the Introspection documentation, which states:
The default NSObject implementation of isEqual: simply checks for pointer equality.
Of course, as you are doubtless aware, subclasses of NSObject can override isEqual: to behave differently. For example, NSString's isEqual: method, when passed another NSString, will first check the address and then check for an exact literal match between the strings.
The answer about default implementation of isEqual: is comprehensive one. So I just add my note about default implementation of hash. Here it is:
-(unsigned)hash {return (unsigned)self;}
I.e it's just the same pointer value which is used in isEqual:. Here's how you can check this out:
NSObject *obj = [[NSObject alloc] init];
NSLog(#"obj: %#",obj);
NSLog(#"hash: %x",obj.hash);
The result will be something like this:
obj: <NSObject: 0x16d44010>
hash: 16d44010
Best Regards.
BTW in iOS 8 hash became a property not a method, but it's there.
I would assume that NSObject isEquals uses the == operator, and hash uses the memory address.
isEquals method should never uses hash as an absolute test for equality. It is guaranteed to have two objects having similar hashCode, if you search for enough objects (just create more than 2^32 different objects, and at least two of them will have the same hash).
In other words, hash requires the following spec: If two objects are equals, then their hash needs to be equal; however, if two objects' hash values are equals, they are not necessarily equal.
As a tip, you always should override isEquals and hashCode together.