As I learn more about KVO and KVC, I have become curious -
How does NSObject provide automatic KVO when accessing setter methods?
If I create a new object with an accessor named setName,
how does an observer get notified when someon calls
[obj setName:#"Mystery"];
Thanks for any feedback
I always explain to people that "nothing is magic in Cocoa; it's just code." But KVO borders on magic. It's called isa-swizzling. Your class is transformed at runtime (the first time anyone observes you) into a dynamically generated sub-class that overloads all getters and setters. Calls to -class are wired to lie to you and return the old class, so you won't see the magic subclasses except in the debugger if you look directly at the isa pointer.
Noticing that KVO must be bizarre is a major step in Cocoa enlightenment. Congratulations.
Key-Value Observing Implementation Details
Related
As a newbie to Cocoa and Objective-C I have a rudimentary understanding of KVC and KVO. However with respect to Cocoa Bindings (as covered in the Apple document titled "Cocoa Bindings Programming Topics" see figures 8-10) I'm unclear why they are depicting using both KVC and KVO, when it seems that KVO would be sufficient. KVO's ObserveValueForKeyPath:ofObject:change:context can provide the old and new values so why is KVC mechanisms needed? Note, I see how KVO decouples objects, but so does KVC.
The example Apple gives (figures 8-10) depicts a Window containing a slider and a text input control to visually represent and allow user interaction for setting&viewing "temperature", a Controller object, and a Model Object with a temperature property. So put another way my question is why not just have a bi-directional KVO relationships between the 2 controls and the controller (each registers with the other as an observer), and bi-directional KVO relationships between the Model Object and the Controller? Why is KVC needed?
The long-winded docs are confusing you.
All this does is about code-reusability.
(1) Provide a standard way to declare and manage properties.
(you can do it manually the old way with ivars and setters and getters, but property synthesis gives it to you for free)
You cannot Observe the Key Value Pairs reliably unless they follow a convention.
The convention is KVC. Following that is being KVC compliant.
(2) Provide a highly reusable and generic way for objects to receive notifications about changes to a property in another object. This is KVO.
KVO is the ability to generically code notifications based on changes in properties that are KVC compliant first.
(3) Bindings & Core Data. Both technologies are built on KVC and KVO to make this all work in as generic a way as possible.
It is also quite similar conceptually to ORMs like Active Record and Ruby on Rails.
The magic starts with KVC.
KVC enables a simple KVO mechanism.
KVO + KVC make Bindings and Core Data possible and easy.
They also provide a lot of syntactic sugar and wacky conveniences.
You can treat the interface to KVC compliant objects as a dictionary or an array.
Then all the patterns fall into place.
You can still have other bi-drectional observer patterns.
Delegation (setting eachother as or sharing a delegate) and Notification (via NSNotification), or even simply messaging other objects (likely bad tight coupling if this is your pattern everywhere, leading to these other patterns being created)
These are not wrong, but have some trade-offs.
Notifications can be spaghetti code at times. Like all callbacks, you end up with something like goto sometimes. However, it isn't necessarily as tightly coupled to a specific property of a specific object like KVO. It is just waiting for a potentially very general notification that could contain a lot of different things. However, by its nature, Notifications tend to be more use-case specific, and easy to apply to custom scenarios.
KVO as-a-specific-technology is built on KVC conventions, and does not work without them.
It makes some very basic, common boiler-plate code & tight coupling easier to create.
A few have tried in the comments, he's my go:
KVO is essentially built on top of KVC - when a KVC compliant property is changed if there is an observer then the KVO machinery kicks in, constructs the info dictionary as needed, and sends the message to the observer(s).
If they question is why was it done this way and why not another then that is a different question. KVO needs to plug into something - you can't just observe changes to a variable (memory location) in a simple way[*]. A property, having a setter & getter, is a place things such as KVO can hook in. And properties follow the KVC pattern and there is already machinery to support that... But that doesn't mean KVO has to depend on KVC, other implementations strategies are undoubtedly possible.
HTH at little.
[*] entering the debugging mode and deploying watchpoints is not "simple" in this context!
I have a custom class that exposes an NSString property. In Interface Builder I've bound the title of an NSButton to the property of my custom class.
Is it possible to get a reference to the NSButton instance from within my custom class?
Essentially I'm trying to locate all the user interface elements that are bound to the property in my custom class.
In general, this sounds like an anti-pattern and/or a bad idea. That said, there are a couple of things to bear in mind. Multiple observers could be bound to your property. You can override addObserver:forKeyPath:options:context: and removeObserver:forKeyPath: (and removeObserver:forKeyPath:context:) and then maintain your own array of observers. With that approach I would caution you that you may need to go to extra effort for the array to not retain observers, as traditionally KV observations don't retain the observing object, and you will likely run into leaks/heap growth if you start retaining them by putting them in an NSArray.
The other gotcha with overriding addObserver:... and removeObserver:... is that, without considerable extra work, you wont know if the observation is for a binding or for something else (like, say, a dependent keyPath notification). One possible workaround for that would be to interrogate the observer via infoForBinding: on all exposedBindings on a later runloop pass using performSelector:afterDelay:. (I think I just threw up in my mouth a little bit for suggesting this.)
Relying on private implementation details of the KVO system is not likely to be a good approach, unless your goal is simply to better understand how KVO works, but it sounds like you're actually trying to accomplish something.
Really, this whole approach just feels like a recipe for disaster. It sounds like an MVC violation from the get-go. Why would the model object need to know about the view objects? Whatever you're trying to accomplish here would almost certainly be better accomplished by having the nib be owned by an NSViewController subclass which has IBOutlets for all the UI elements, and properties for the model. That object would then be in a position to more cleanly manage the apparently complex relationship between your view and model objects without runtime trickery. Since you've not elaborated on the ultimate goal of this trickery, it's hard to say what the best approach would be.
I got answer about Foundation magic for this question: What's the most *simple* way to implement a plain data object which conforms key-value-observing?
What's the magic? How it work internally? Because it's dangerous using framework which I can't understand its internal behavior, I want to know its behavior. Currently, I cannot understand how it work without any method definitions.
Apple's documentation describes how KVO is implemented internally.
The gist of it is that when you register an observer on an object, the framework dynamically creates a subclass of the object's original class, and adjusts the object to appear as an instance of this new dynamic class. You can see this if you inspect an object in the debugger after it has had an observer registered.
This new class intercepts messages to the object and inspects them for those matching certain patterns (such as the getters, setters, and collection access).
In a nutshell: Objective-C 2.0's #property declaration creates accessor methods for the named property, so there are method definitions. #property is just a shorthand way to define them which avoids a lot of repetitious boilerplate code.
When you observe a property, a private subclass is created which implements accessors that call the appropriate notification methods before and after changing the property value. A technique known as "isa swizzling" is then used to change the class of the observed object.
What do I need to do to update a tableView bound to an NSArrayController when a method is called that updates the underlying array? An example might clarify this.
When my application launches, it creates a SubwayTrain. When SubwayTrain is initialised, it creates a single SubwayCar. SubwayCar has a mutable array 'passengers'. When a Subway car is initialised, the passengers array is created, and a couple of People objects are put in (let's say a person with name "ticket collector" and another, named "homeless guy"). These guys are always on the SubwayCar so I create them at initialisation and add them to the passengers array.
During the life of the application people board the car. 'addPassenger' is called on the SubwayCar, with the person passed in as an argument.
I have an NSArrayController bound to subwayTrain.subwayCar.passengers, and at launch my ticket collector and homeless guy show up fine. But when I use [subwayCar addPassenger:], the tableView doesn't update. I have confirmed that the passenger is definitely added to the array, but nothing gets updated in the gui.
What am I likely to be doing wrong? My instinct is that it's KVO related - the array controller doesn't know to update when addPassenger is called (even though addPassenger calls [passengers addObject:]. What could I be getting wrong here - I can post code if it helps.
Thanks to anyone willing to help out.
UPDATE
So, it turns out I can get this to work by changing by addPassenger method from
[seatedPlayers addObject:person];
to
NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];
[newSeatedPlayers addObject:sp];
[seatedPlayers release];
[self setSeatedPlayers:newSeatedPlayers];
I guess this is because I am using [self setSeatedPlayers]. Is this the right way to do it? It seems awfully cumbersome to copy the array, release the old one, and update the copy (as opposed to just adding to the existing array).
I don't know if its considered a bug, but addObject: (and removeObject:atIndex:) don't generate KVO notifications, which is why the array controller/table view isn't getting updated. To be KVO-compliant, use mutableArrayValueForKey:
Example:
[[self mutableArrayValueForKey:#"seatedPlayers"] addObject:person];
You'll also want to implement insertObject:inSeatedPlayersAtIndex: since the default KVO methods are really slow (they create a whole new array, add the object to that array, and set the original array to the new array -- very inefficient)
- (void)insertObject:(id)object inSeatedPlayerAtIndex:(int)index
{
[seatedPlayers insertObject:object atIndex:index];
}
Note that this method will also be called when the array controller adds objects, so its also a nice hook for thinks like registering an undo operation, etc.
I haven't tried this, so I cannot say it works, but wouldn't you get KVO notifications by calling
insertObject:atArrangedObjectIndex:
on the ArrayController?
So, it turns out I can get this to work by changing by addPassenger method from
[seatedPlayers addObject:person];
to
NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];
[newSeatedPlayers addObject:sp];
[seatedPlayers release];
[self setSeatedPlayers:newSeatedPlayers];
I guess this is because I am using [self setSeatedPlayers]. Is this the right way to do it?
First off, it's setSeatedPlayers:, with the colon. That's vitally important in Objective-C.
Using your own setters is the correct way to do it, but you're using the incorrect correct way. It works, but you're still writing more code than you need to.
What you should do is implement set accessors, such as addSeatedPlayersObject:. Then, send yourself that message. This makes adding people a short one-liner:
[self addSeatedPlayersObject:person];
And as long as you follow the KVC-compliant accessor formats, you will get KVO notifications for free, just as you do with setSeatedPlayers:.
The advantages of this over setSeatedPlayers: are:
Your code to mutate the set will be shorter.
Because it's shorter, it will be cleaner.
Using specific set-mutation accessors provides the possibility of specific set-mutation KVO notifications, instead of general the-whole-dang-set-changed notifications.
I also prefer this solution over mutableSetValueForKey:, both for brevity and because it's so easy to misspell the key in that string literal. (Uli Kusterer has a macro to cause a warning when that happens, which is useful when you really do need to talk to KVC or KVO itself.)
The key to the magic of Key Value Observing is in Key Value Compliance. You initially were using a method name addObject: which is only associated with the "unordered accessor pattern" and your property was an indexed property (NSMutableArray). When you changed your property to an unordered property (NSMutableSet) it worked. Consider NSArray or NSMutableArray to be indexed properties and NSSet or NSMutableSet to be unordered properties. You really have to read this section carefully to know what is required to make the magic happen... Key-Value-Compliance. There are some 'Required' methods for the different categories even if you don't plan to use them.
Use willChangeValueForKey: and didChangeValueForKey: wrapped around a change of a member when the change does not appear to cause a KVO notification. This comes in handy when you are directly changing an instance variable.
Use willChangeValueForKey:withSetMutation:usingObjects: and didChangeValueForKey:withSetMutation:usingObjects: wrapped around a change of contents of a collection when the change does not appear to cause a KVO notification.
Use [seatedPlayers setByAddingObject:sp] to make things shorter and to avoid needlessly allocating mutable set.
Overall, I'd do either this:
[self willChangeValueForKey:#"seatedPlayers"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:sp];
[seatedPlayers addObject:sp];
[self didChangeValueForKey:#"seatedPlayers"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:sp];
or this:
[self setSeatedPlayers:[seatedPlayers setByAddingObject:sp]];
with the latter alternative causing an automatic invocation of the functions listed under 1. First alternative should be better performing.
I'm using Cocoa bindings to manage a table of objects. I understand how bindings work but I've run into a slight problem. Managing the table of objects would be fine and dandy, except that those objects have to manage actual bluetooth hardware. I'm working off of a framework that provides a class representing a connection to this hardware, and have made another "manager" class the makes it key-value compliant. In other words, this manager class has to be able to connect and modify its "connect" status in its properties dictionary, be the delegate of this hardware and modify properties, and update the hardware with changes made.
However, whenever I set new values within the object itself, like in a "connect" method that would change the "connect" key's value to 2 (looking), (i.e. propertiesDict = newDict), the change is not seeming to be picked up by observers that it is bound to. I've looked at the observeValueForKeyPath:ofObject:change:context: in the NSKeyValueObservingProtocol. However, I don't know what to do with the context argument.
I hope that makes sense... but if anyone has any ideas I'd love to hear them.
Your question isn't totally clear, but if I'm understanding it correctly the issue might be because you need to send manual KVO notifications before and after you change a value in the embedded object. For instance, [self willChangeValueForKey:#"connected"]; and [self didChangeValueForKey:#"connected"];.
There are three ways to update a property/attribute in a KVO compatible way:
Using the property setter (specified in #property declaration or generated by #synthesize)
Calling -willChangeValueForKey: and -didChangeValueForKey: before and after you change the property value in any way.
Calling -setValueForKey: