Since Objective-C 2.0, we've had #properties and autogenerated accessor methods. So today, what is the point of key-value coding? Under what circumstances is it preferable to write
[myObject setValue:foo forKey:#"bar"];
instead of writing
[myObject setBar:foo];
or even
myObject.bar = foo;
I keep seeing articles and documentation making use of KVC, but always in a way where it seems like simply using properties would be better. So why would I ever use KVC? Thanks for any and all insight.
It's almost never preferable to write out [myObject setValue:foo forKey:#"bar"] by hand, with a literal #"bar". We usually use KVC to access a property when we don't know which property we want to access until runtime.
One example is an outlet in a xib. When you connect a text field's delegate outlet to the File's Owner in the xib, the xib records the connection as an object with three fields:
a reference to the text field (the object that has the outlet)
a reference to the file's owner placeholder (the destination of the connection)
the name of the outlet as a string, #"delegate"
At runtime, the xib loader (part of the UIKit framework) deserializes the text field. Then it deserializes the connection object and uses it to establish the connection that you wired up in the xib. The xib loader has to set a property of the text field (the delegate property), but it doesn't know which property until it loads the xib at runtime, long after both your app and the UIKit framework were compiled.
Another example of not knowing which property to access until runtime is the (little-known) ability of Core Animation to animate a custom property of your CALayer subclass. Say you create a subclass of CALayer called PolygonLayer, with a property named sides. You can animate the sides property using a standard CABasicAnimation:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"sides"];
animation.fromValue = #3;
animation.toValue = #9;
animation.autoreverses = YES;
animation.duration = 1;
[myPolygonLayer addAnimation:animation forKey:#"sides"];
Presto, Core Animation will animate your layer's sides property from 3 to 9 and back. Yet the source code of Core Animation doesn't know anything about your sides property. (Check out this question for more details.)
There are times we use KVC even though we know the property at compile-time. One example is when we want to take advantage of extra work KVC will do for us. For example, if you have an NSArray full of Person objects, and you want to get an array of every person's first name, you could write this:
NSMutableArray *firstNames = [NSMutableArray array];
for (Person *person in people) {
[firstNames addObject:person.firstName];
}
But this is a case where KVC has a feature that makes it simpler. If you access a property of an array using KVC, KVC will actually access that property of every element in the array for you:
NSArray *firstNames = [people valueForKey:#"firstName"];
Another example where we might use KVC even though we know the property at compile-time is when the property is not statically declared as part of the class. For example, each NSManagedObject (part of Core Data) dynamically gives itself properties based on whatever entity that instance of NSManagedObject is representing. You can access those properties using KVC, although generally we prefer to declare them in a subclass of NSManagedObject or in a category of NSManagedObject.
KVC can be useful if you are using Key Value Observers to detect value changes on an object. If you wanted to use KVO and #properties you would have to wrap every mutator method with:
[self willChangeValueForKey:#"bar"];
bar = foo;
[self didChangeValueForKey:#"bar"];
Sometimes you don't know what property you want to set/get until run-time.
In this case you can use KVC by constructing the property key path as a string.
For example i have an object with multiple NSArray properties and i want to keep the last NSDate they were updated.
Let's say i have an array property called: comments and an array property called likes.
I define a properties: commentsLastModified and likesLastModified.
when an array is updated (i have the property name as string), i use:
[object setValue:[NSDate date] forKey:[NSString stringWithFormat:#"%#%#", arrayString, #"LastModified"];
Related
I'm was playing around with the standard sample split view that gets created when you select a split view application in Xcode, and after adding a few fields i needed to add a few fields to display them in the detail view.
and something interesting happend
in the original sample, the master view sets a "detailItem" property in the detail view and the detail view displays it.
- (void)setDetailItem:(id) newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
i understand what that does and all, so while i was playing around with it. i thought it would be the same if instead of _detailItem i used self.detailItem, since it's a property of the class.
however, when i used
self.detailItem != newDetailItem
i actually got stuck in a loop where this method is constantly called and i cant do anything else in the simulator.
my question is, whats the actual difference between the underscore variables(ivar?) and the properties?
i read some posts here it seems to be just some objective C convention, but it actually made some difference.
_property means you are directly accessing the property.
self.property means you are using accessors.
In your case, in the setter method you are calling it, creating a recursive call.
In the course of your experiment, you've set up an endless loop which is why the simulator goes non-responsive.
Calling self.detailItem within the scope of setDetailItem: calls setDetailItem: recursively since your class implements a custom setter method for the property detailItem.
I would refer you to the Apple documentation on declared properties for the scoop on properties, ivars, etc; but briefly, declared properties are a simplified way of providing accessor methods for your class. Rather than having to write your own accessor methods (as we had to do before Objective-C 2.0) they are now generated for you through the property syntax.
The properties are basically a way of the compiler to generate a setter and getter for a given instance variable.
So when you use something like:
id detailItem = self.detailItem;
what you are doing under the hood is:
id detailItem = [self detailItem];
Same for:
self.detailItem = otherDetailItem;
would be:
[self setDetailItem:otherDetailItem];
So when you write the setter yourself.. you get in an infinite loop since you access the method itself in itself.
You can freely make use of the 'self.' notation in your class, just not when you're overriding the setter or accessor because of the mechanism I described above.
Cases in a class where I use the . notation over simply accessing the ivar is when I change the value, you never know inside your class what needs to happen when you change the value. do you have something in terms of a status that should notify some delegate that a status changed? Usually this is not the case, however, just by using the . notation you are making sure that in the future you won't have to refactor some code if you did decide to do some magic in your setter method.
I'll make an example (without ARC enabled):
#property (nonatomic, retain) NSNumber* number;
If you don't synthesize it, you can access it this way:
self.number= [NSNumber numberWithBool: YES];
This case the number is retained.If instead you synthesize it and don't use the property:
#synthesize number;
Later in the file:
number=[NSNUmber numberWithBool: YES];
You haven't used the property, so the number is not retained.That makes a relevant difference between using accessors and synthesized properties.
OK, so I'm implementing a classic scenario :
A NSPopupButton with some items in it
When the selected value changes, my itemsArray is updated
The itemsArray is linked to an NSArrayController
Each item in the itemsArray is an NSMutableDictionary (with keys : title,content)
An NSTableView displays the titles of the arrangedObjects (binding)
An NSTextView displays the content of the selected item.
Now, what I want is to automatically save any changes to the itemsArray (or itemsArray's item title/content), but without using Core Data (which I suspect might have been the best way to go about it).
I imagine it's quite a basic question this one, but honestly I've never really like Cocoa's auto-magical way of doing things... so, I need your help...
How should I go about that?
You can write an array to a file very easily:
[yourArray writeToURL:someFileURL atomically:YES];
This will work if all the contents of the array are property list objects (i.e. they are NSNumber, NSString, NSDictionary, NSArray or NSData objects). This is the case in your example.
You can then recreate the array using either the arrayWithContentsOfURL: or initWithContentsOfURL: methods when you load from disk.
If your model object is more complex than just an array, then you should make your model object conform to the NSCoding protocol. This means you need to implement the initWithCoder: and encodeWithCoder: methods. You can then use the NSKeyedArchiver and NSKeyedUnarchiver classes to convert your object to and from an NSData representation that you can write to disk.
You should read the Archives and Serialization Programming Guide for more detailed information.
Another solution might be to add a Shared User Defaults Controller and bind the Controller Content Array from the Array Controller to the Shared User Defaults Controller
I have some Core Data functionality that was working fine until some recent (seemingly unrelated) changes were made. Now I'm getting problems where all the attributes belonging to a particular NSManagedObject subclass instance are suddenly returning nil.
Let's say my NSManagedObject subclass is called Foo and it has only one attribute called value. Once I realised value was somehow becoming nil I went and setup the following category to monitor changes to value.
#implementation Foo (Debug)
- (void)setValue:(NSDate *)value
{
[self willChangeValueForKey:#"value"];
[self setPrimitiveValue:value forKey:#"value"];
[self didChangeValueForKey:#"value"];
}
- (NSDate *)value
{
[self willAccessValueForKey:#"value"];
NSDate *value = [self primitiveValueForKey:#"value"];
[self didAccessValueForKey:#"value"];
return value;
}
#end
setValue: is called for my object and the argument passed in is a non-nil NSDate. Then the value is retrieved (in another method). The same value that was specified is retrieved correctly.
However when another method tries to read value, the value accessor is called and a nil value is returned by primitiveValueForKey:.
In between the two reads setValue: is not called and the Foo object itself is still valid (non-nil). In fact no other Core Data operations are performed between the two reads on any Core Data object or the context as a whole.
We're using ARC in our project. Is it possible ARC is somehow messing with my Core Data variables and deallocating them? If so does anybody have any suggestions for debugging ARC deallocations? Or better yet, does anyone know a way to ensure ARC doesn't deallocate my variable.
This may not even be ARC related, however I'm at a bit of a loss as to what is going on. Any suggestions would be very much appreciated.
This is very likely because the NSManagedObjectContext that these objects belong to, is going away. When you have NSManagedObject instances around but you're not holding on to the context yourself, those managed objects will start returning nil.
Under ARC, make sure you store the context in a strong variable, i.e. an instance variable that's not weak or a static global.
Non-ARC, i.e. retain-release code, make sure you're retaining the context.
As others mentioned (it was my case also), be sure that you haven't reset your managed object context because if you do, all Entities stored as properties will have data: <fault>.
If you do reset your managed object context, you will also have to re-fetch the Entity itself.
check the viewDidLoad-Method
profile = [NSEntityDescription insertNewObjectForEntityForName:#"MyProfile" inManagedObjectContext:profileContext];
hope this works
Lets say I have an object with a number of properties, one of them being a CALayer.
Throughout my view, I have to hitTest layers. After I've got the layer, I then need to get at the object of which the layer is a property, or 'to which it belongs'. Is there anyway to return the owner of a property?
Thanks!
We have three different things here:
The property
The instance variable behind the property
The CALayer
These are three distinct things. The property belongs to the object's class, and tells the compiler how to go about accessing the instance variable (if I can be a little bit hand-wavy). The instance variable belongs to the object, and points to the CALayer. And the CALayer just does its own little CALayer thing.
Several different instance variables used by any number of properties either from the same object or many different objects can all point to that same CALayer object.
So the question becomes: Does an object keep a list of all the variables that are pointing to it?
And the answer is: Unfortunately, no.
One approach is to iterate through your objects, comparing the layer property to the CALayer retrieved by hit-testing:
MyObject *theObject = nil;
for ( MyObject *obj in self.objects ) {
if ( obj.layer == theLayer ) {
theObject = obj;
break;
}
}
Another approach is to subclass CALayer and add ivar/property pointing to the object it represents. To avoid retain cycles it should be #property(assign) MyObject* representedObject. It makes getting the object trivial, but requires subclassing CALayer.
I wold suggest you pass around the parents of the CALayers instead of the actual layers when you do the hit-testing, so that you never need to back up to the parent.
You ca not find the parent of a property object, unless you implemented a both-ways relation. Both-ways relations are generally bad, since it can easily introduce retain cycles.
The general solution to this in Cocoa is to use delegates, turns out that the delegate for a CALayer is the UIView it is backing. So in the case of most CALayer instances you can back up to it's parent through the delegate. Do notice I say most, not all, so it is not a silver bullet.
I have a pretty standard setup where I have an array in my controller that I'm binding to an NSTableView. When I add an object to this array with the UI, I add elements by copying the array to a mutable array, adding the contents, then copying it back. I saw this idiom somewhere, but I'm not really sure where, or whether this is actually a sane thing to do. I t works fine on Snow Leopard, but it crashes really badly on Leopard. GDB tells me it happens right at the marked line, where I copy the new array back.
#interface MyAppDelegate : NSObject {
NSArray * urls;
//other declarations
}
#property (nonatomic, retain) NSArray *urls;
#implementation MyAppDelegate
#synthesize urls;
- (void)addThing:(id)sender {
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
}
I'm pretty sure I must be doing something boneheaded here. Is there a better way to do this? Why am I crashing?
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
What did you create newUrls for if not to set it as the new value of urls?
Besides that, there are a couple of things you're doing wrong:
No model objects. Bindings hates this. Unless your table view exists solely to display the different parts of the URL (scheme, host, path, etc.), each in one column, you're not giving Bindings what it wants.
Pay attention to the fields in the Bindings Inspector. Note that there are two: Controller Key and Model Key Path. They're exactly what they say on their tins: The Controller Key is the key for a property of the controller, which should provide model objects. The Model Key Path is the key path for one or more properties in the model, which usually should provide value objects, such as strings and numbers.
You probably need to create a model class and make the URL a property of that class. I'd guess that you have other objects, perhaps in parallel arrays, that you can move to properties of the model class. Do that, so that you're working with Bindings instead of against it.
Not using array accessors. You're just setting the whole array at once, which is inefficient and may cause display problems (such as selection highlights disappearing). Implement and use array accessors for this array property. Accessorizer can help you here.
I actually have no idea whether this will fix your crash because you haven't told us anything about it. You should edit your question to include any exception messages or other crash-describing output, which you'll find in the Run Log/Debugger Console.
Also, since the type of urls can be mutable, you should set it to copy, not retain.
NSArray * things; - since this can be modified you better represent using NSMutableArray instead NSArray.
When ever you need to add some new element to the list just use 'addObject' and insert element to the list.
Your binding will make sure that UI is updated using KVO and KVC.
It looks like the problem was that I had NSURLs as my object type. Once I changed the object in my array to a custom-made KVC compliant object, I wasn't getting any more crashes.
Maybe NSURL isn't KVC-compliant on 10.5, but it is on 10.6?