According to Ensuring KVC Compliance,
For properties that are an attribute or a to-one relationship, this requires that your class:
Implement a method named -<key>, -is<Key>, or have an instance variable <key> or _<key>.
What is the best way to add observers to "FAKE" properties like the following?
#property (nonatomic, readonly) BOOL shortlisted;
#pragma mark - Fake properties
- (BOOL)shortlisted
{
return [self.provider isJobShortlisted:self];
}
Dependent Keys
If you simply have one key which is dependent on another key of the same object, you can override +keyPathsForValuesAffectingValueForKey:. (See also this article for a common pitfall.)
The KVO documentation has information about keys dependent on other objects, but unfortunately it looks like this only has built-in support on OS X (not iOS).
Manual Notifcations
If you're implementing a completely custom property, the section on Manual Change Notification in the KVO docs has all the information you need. The key points are (with quotes from the docs):
A class that implements manual notification must override the NSObject implementation of automaticallyNotifiesObserversForKey:. ... For properties that perform manual notification, the subclass implementation of automaticallyNotifiesObserversForKey: should return NO.
To implement manual observer notification, you invoke
willChangeValueForKey: before changing the value, and didChangeValueForKey: after changing the value.
(If you change multiple properties, you can nest these calls.)
In the case of an ordered to-many relationship, you must specify not only the key that changed, but also the type of change and the indexes of the objects involved.
For this you use the methods willChange:valuesAtIndexes:forKey: and didChange:valuesAtIndexes:forKey:.
Related
Below two different paragraphs from Apple docs. In one paragraph, it says all class which inherits from NSObject to use KVO, is KVO compliant. In the second paragraph, it says not all classes are KVO compliant. Which are those classes which not KVO compliant? Is there any class which does not inherit NSObject? Whereas I know all inherit from NSObject.
It would ideal to give an example, to understand the difference between the two paragraphs.
To use KVO, first you must ensure that the observed object, the
Account, in this case, is KVO compliant. Typically, if your objects
inherit from NSObject and you create properties in the usual way, your
objects and their properties will automatically be KVO Compliant. It
is also possible to implement compliance manually. KVO Compliance
describes the difference between automatic and manual key-value
observing, and how to implement both.
and
Important: Not all classes are KVO-compliant for all properties. You
can ensure your own classes are KVO-compliant by following the steps
described in KVO Compliance. Typically properties in Apple-supplied
frameworks are only KVO-compliant if they are documented as such.
For a property of an object to be KVO-compliant, the object must inherit NSObject and the object must also either:
only update the property by using the property's setter method, or
manually notify when it modifies the property.
Since you don't have the source code for Apple's frameworks, you cannot in general know whether an object of an Apple-provided class obeys either of these requirements, except by checking the documentation. If the documentation says a property is KVO-compliant, you know it complies. Otherwise, you don't know if it complies so it is not safe to use KVO on the property.
It's important to understand that a property might sometimes be updated in a KVO-compliant way, and other times in a non-compliant way. So you can't just do a simple test to decide! Your test might show that the property is KVO-compliant for the way you set the property, but it cannot show that the property is always updated in a KVO-complaint way.
For example, every UIView owns a CALayer, and many of the UIView's properties, including its frame, its bounds, and its backgroundColor are actually properties of that CALayer. When you get or set the property on the view, the view's accessor method just sends the message on to the layer. But you can also set the layer's property directly. So if you say view.bounds = someRect, the view probably will notify KVO observers. But if you say view.layer.bounds = someRect, the view will not notify KVO observers. But after either of these statements, view.bounds will return someRect.
So, you can only rely on a property to be KVO-compliant if you are responsible for the implementation of that property, or if the property is documented to be KVO-compliant.
Key-Value Coding and Key-Value Observing are both dependent on naming conventions to identify which accessor methods correspond to which properties.
If you use declared properties (using the #property keyword) and you don't override the accessor method names, then the accessor methods will comply with the naming conventions and KVC and KVO will be able to identify the methods associated with a key. If you override the method names or use informal properties, then you are responsible for complying with the naming conventions. (One common case for this is using getter=is<Key> for a boolean property. It's an overridden getter name, but it still complies with the naming conventions.)
In addition to using accessor methods with conventional names, a class must also actually use the accessors to modify its own properties in order to take advantage of automatic change notification. The class should not directly modify its instance variables (outside of init methods or -dealloc) or, if it does, it needs to use manual change notification for that property.
What the first quote you cite is saying is that you get a lot of automatic behavior from NSObject, assuming you follow the naming conventions and modify your properties via their accessors rather than instance variables. That's what "create properties in the usual way" is getting at.
What the second quote is saying is that you can't assume that Apple's own classes do that. In some cases because of historical implementation details and in general for flexibility, they reserve the right to make their classes' properties non-compliant. You must not assume that the properties are KVO compliant unless they are specifically documented to be so.
Whenever a property on my object is changed or updated I want to change a variable (nonce variable). This nonce is time-based. So everytime a property is updated this nonce gets updated to the current time.
Is there any way to automatically listen for all key changes on my object? Or will I have to manually maintain all keyvalue observers for each property separately?
Many thanks
Did you take a look at the Obj-C runtime functions? See here in the docs. For example, this gives you a list of all the properties in a class Lender. (BTW: I'm not at my Mac, so this is just straight out of the docs):
#interface Lender : NSObject {
float alone;
}
#property float alone;
#end
you can get the list of properties using:
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
you can then get the name of a property:
const char *property_getName(objc_property_t property)
If you pipe those names back into addObserver:forKeyPath:options:context you should be golden.
Some ideas:
1) you can ask the runtime for the properties and ivars, and their types, and use that information to create and take down observers. Obviously a lot of work if you are doing this for one object.
2) if your properties are "regular", meaning all strong objects, then you can use #dynamic (to prevent setter/getter creation), then use resolveInstanceMethod: or other funky methods from NSObject to catch setMyObject: and myObject calls. You could in essence do what the system does for 'set...' calls, and dynamically get the string of the variable. You could then update/get an ivar, maybe one that has a prefix of "-" or something, and you'd be able to do what your observers would be doing.
3) You could put all the ivars in a "Helper" class, and direct all the setters to it (which could of course message you back), using forwardingTargetForSelector:. I'm using this technique (sort of) in one of my github projects
So I want to have a "property" on a class but I don't want to just hold that property in memory, I want to actually store it as an NSUserDefault and retrieve it when you get that property.
So as such I have methods like this:
- (void)setUser:(User *)user {
// actually set the user as an NSUserDefault here
}
- (User *)user {
// get the user from the NSUserDefaults and return it
}
As I'm building these methods to do the work for me is there any point in having an #property declaration in the header file?
I'm getting mixed messages. Some people say that you should declare the property to force people to use the getter/setter methods, but I can't see why people wouldn't be forced to use those methods if they're all that are available?
Just looking for a bit of clarification.
Many thanks.
You should use #property because that's the modern way to define properties on Objective-C objects, even if you implement the setter and getter yourself.
Rather than relying on convention you are making your intentions much clearer to the compiler. You will also get better syntax highlighting when using dot-notation in the IDE (although that's arguably an Xcode bug).
I understand the key differences/process. A property creates the getters and setters for a class variable. It also (now) synthesises it with a private variable with a _ prefix.
Great.
Now, I want some methods to act like a property. So I have a UIView subclass which has a UILabel subview.
I want to create the two methods - (NSString *)text and setText:(NSString *)text in order to set and get the text of the UILabel.
Obviously this is ACTING like a property (you can do [myCustomElement text] and [myCustomElement setText:#"whatever"]) so I feel like I should define a property, but what use would this have, if any?
My getters and setters will look like this:
- (NSString *)text {
return self.textLabel.text;
}
- (void)setText:(NSString *)text {
self.textLabel.text = text;
}
You could do this, but I would discourage you from doing so. To have methods that look like standard accessor methods (getters and setters), but are really updating UI controls is only going to be a source of confusion in the future. This only undermines your code's readability when methods are performing functions that don't conform to conventional practices. Plus you already have a property for your textLabel, so these methods don't buy you very much.
By the way, the standard auto-generated accessor methods provide other useful functions (doing the necessary memory management on the basis of the qualifiers you specify when you define the property, enabling key value observation for some future date where you might need such functionality, etc.), so I would be reticent to replace them with your own methods unless you are expert in these concepts.
If you're doing stuff different than the standard accessor methods, I'd suggest using method names that are less likely to be a source of confusion in the future.
text is already a property (called text) of yourCustomElement property textLabel ( that's why you access it using dot notation yourCustomElement.textLabel.text)
in this example the getters/setters for the text property should/can only be inside the textLabel Class
Those two methods are just convenience methods to set/get a property of a property (fine, but they're not getters/setters and shouldn't look them)
I think for your specific example, it's fine not to define a property.
Properties (as far as I can work out) provide handy shorthands that ensure proper retain/releasing (when appropriate) under the hood.
http://useyourloaf.com/blog/2011/02/08/understanding-your-objective-c-self.html
what use would this have, if any?
There are many Uses :
1) You can use properties to do some Calculations , Updating Object's State , or some Caching like stuff.
2) Have you heard about Key-Value Coding (KVC) and Key-Value Observing (KVO) in Cocoa ? They are depended on Properties. Check : KVC Programming Guide and KVO Programming Guide.
3) Using properties you could put some memory management code in the accessors.
If you declare it as a property, and override the getters/setters, then you have access to using dot notation to change or retrieve the value.
Ex:
myCustomElement.text = #"whatever";
long storry short:#synthesis generates getters and setters. So using synthesis saves you typing.
Your code however is not the same as #synthesis textLabel because your code allows only changing the labels text. #synthesis would allow changing all properties.
Here is some useful information about #synthesis / getters / setters
http://useyourloaf.com/blog/2012/08/01/property-synthesis-with-xcode-4-dot-4.html
Perfect KVO here includes two parts: add observer correctly and remove observer correctly.
The story:
I use one UITableViewCell(cell) to display one NSManagedObject(object).
Each object has some dynamic properties that need observing by its cell.
Not all objects have the same set of observed properties. I add key path observers selectively like this:
if (object.thumbnail_pic_url)
[object addObserver:cell forKeyPath:#"thumbnail_picture" options:0 context:NULL];
Object could be deleted. I must remove observers when object is deleted. The database is very large and complex so I definitely don't want to register all cells to receive moc notifications like NSManagedObjectContextObjectsDidChangeNotification. But I can accept to add a cell ivar in object if I have to, even though it goes agains good Modle-View-Controller design pattern.
The problem: How can I correctly remove the observer(cell) for all the registered key paths from an object when it is deleted?
In fact, it is a big problem that can be divided into two small problems:
Where is the best place to put the observer removing code?
How do I determine which key paths to unregister? I can't query its properties after an object is deleted — it will cause unfulfillable faults, so I can't write code like this:
if (object.thumbnail_pic_url)
[object removeObserver:cell forKeyPath:#"thumbnail_picture"];
and I can't either blindly remove observer for unregistered key path — exceptions(Cannot remove an observer for the key path "thumbnail_picture" from because it is not registered as an observer.) will be thrown up.
an0,
There is an NSManagedObject method just for doing deletion timed functions: -prepareForDeletion.
Its documentation claims: "You can implement this method to perform any operations required before the object is deleted, such as custom propagation before relationships are torn down, or reconfiguration of objects using key-value observing."
You could also look at using: -willTurnIntoFault and -didTurnIntoFault. But I think you'll be happier using -prepareForDeletion.
Andrew
P.S. This method is documented in the class reference. I respectfully suggest that you save time by reading the documentation.
The main problem implementing KVO here is that you don't know when the object is getting deleted , at least not outside the NSManagedObject subclass, What you could really do is create a generic delegate on a subclass of NSManagedObject and override its didChangeValueForKey: method
// DataObservingManagedObject.h
#import <Foundation/Foundation.h>
#import <MMRecord/MMRecord.h>
#protocol DataObservingDelegate <NSObject>
-(void)valueChangedForKey:(NSString*)key andValue:(id)value;
#end
#interface DataObservingManagedObject : NSManagedObject
#property(nonatomic,weak)id<UserStatusDelegate> changeDelegate;
#end
//DateObservingManagedObject.m
#import "DateObservingManagedObject.h"
#implementation DateObservingManagedObject
#synthesize changeDelegate=_changeDelegate;
-(void)didChangeValueForKey:(NSString *)key{
[self.changeDelegate valueChangedForKey:key andValue:[self valueForKey:key]];
}
#end
I believe the mistake is in step 1. Cells should be designed to display different objects. Cells are simply views with labels that can show anything. Tables are optimised to reuse the same cells for different data objects. In your VC create a configureCellWithEvent, configureCellWithVenue methods etc. then you can dequeue a cell of generic identifier and pass it to these methods. Then you likely won’t even have the issue of when to add remove observers because cells shouldn't have object observers.