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.
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!
Say I'd like a mutable, unordered to-many relationship. For internal optimization reasons, it'd be best to store this in an NSMutableDictionary rather than an NSMutableSet. But I'd like to keep that implementation detail private.
I'd also like to provide some KVO-compliant accessors, so:
- (NSSet*)things;
- (NSUInteger)countOfThings;
- (void)addThings:(NSSet*)someThings;
- (void)removeThings:(NSSet*)someThings;
Now, it'd be convenient and less evil to provide accessors (private ones, of course, in my implementation file) for the dictionary as well, so:
#interface MYClassWithThings ()
#property (retain) NSMutableDictionary* keyedThings;
#end
This seems good to me! I can use accessors to mess with my keyedThings within the class, but other objects think they're dealing with a mutable, unordered (, unkeyed!) to-many relationship.
I'm concerned that several things I'm doing may be "evil" though, according to good style and Apple approval and whatnot. Have I done anything evil here? (For example, is it wrong not to provide setThings, since the things property is supposedly mutable?)
I wouldn't make a property (even a private one) for the dictionary, but I don't think there's anything wrong with it.
… is it wrong not to provide setThings, since the things property is supposedly mutable?
Yes. KVC will not like the absence of a setThings: method.
Nothing evil here. The only mandatory mutation methods for an unordered relationship are addThings: and removeThings: (see the KVC doc). The accessors for your keyedThings property won't collide with any KVC accessor, so you're also fine there.
To put your mind at ease, the only things Apple's static analyzer is known to check for are messages to undocumented APIs. Other than that, if your implementation decisions don't affect the app's behavior, you're alright for App Store approval.
Update: I got interested in this question and re-read the KVC doc for myself. The language here gave me pause:
To-many unordered relationships are
most often modeled using instance of
NSSet or a subclass. In that case the
key-value coding will, if it doesn’t
find these accessor patterns for the
property, directly access the set.
Typically, you only implement these
methods if you are using a custom
collection class that needs to be
accessed as if it was a set.
It sounds like the author would prefer that you get rid of things and implement enumeratorOfThings: and memberOfThings:.
After writing a few lesser programs when learning Java the way I've designed the programs is with Model-View-Control. With using MVC I have a plethora of getter methods in the model for the view to use.
It feels that while I gain on using MVC, for every new value added I have to add two new methods in the model which quickly get all cluttered with getter & setters.
So I was thinking, maybe I should use the notifyObserver method that takes an argument. But wouldn't feel very smart to send every value by itself either so I figured, maybe if I send a kind of container with all the values, preferably only those that actually changed.
What this would accomplish would be that instead of having a whole lot of getter methods I could just have one method in the model which put all relevant values in the container.
Then in the view I would have a method called from the update which extracted the values from the container and assigning them to the correct fields.
I have two questions concerning this.
First: is this actually a viable way to do this. Would you recommend me doing something along these lines?
Secondly: if I do use this plan and I don't want to keep sending fields that didn't actually change. How would I handle that without having to have if statements to check if the value is not null for every single value?
I've more familiar with the MVP paradigm, but hopefully they're similar enough to comment. While getters (and setters) in and of themselves are not necessarily evil, they are sometimes a sign that your subsystems are too strongly coupled. One really great way to decouple this is to use an event bus: see Best practices for architecting GWT apps. This allows the view to just shoot off events for the controller to listen for whenever something important happens, and the view can listen for events whenever something changes in the model that corresponds to updating the view. Ideally you wouldn't even need to ever pass the model to the view, if you can break up any changes into incremental pieces and just tell the view to change this part and then this other part.
If you feel you have too many getters (and setters) in your model class, maybe you have too many fields altogether. Is it possible that there are several distinct classes hiding within your model? If you extract these into separate classes, it may make your model more manageable.
OTOH the associated container you are thinking about could also be viable - but then why duplicate all data? You could instead use the associated container directly in the model to store all properties you can think of. And you can also pass this around for observers to get updates (preferably wrapped into an unmodifiable container, of course) - although in this setup you wouldn't need to.
In general, Java is a verbose language which expects you to put all those getters and setters (and a lot more) in place. However, any decent IDE can generate those for you with a few keypresses. Note also that you need to write them only once, and you will read and call them many many more times. Verbose also means easily readable.
If you have too many getter it's ok. But you shouldn't need the setter. The view is supposed to only read/query the model.
The MVC pattern should promote something that is asymmetric: the control update the model by calling methods in the model that embed the logic and update the sate accordingly; this respects encapsulation. The view reads/queries the model via the getters. This goes a bit against information hiding, but that's how MVC works.
I wouldn't personally pass all information in the events. It sounds complicated to me: either you end up with something that is not statically typed (e.g. you pass hashmaps), or with a plethora of typed events. I would stick with something simple, and have (possibly many) getter in the model.
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:
I have an object that implements the indexed accessor methods for a key called contents. In those accessors, I call willChange:valuesAtIndexes:forKey: and didChange:valuesAtIndexes:forKey: when I modify the underlying array.
I also have a custom view object that is bound to contents via an NSArrayController. In observeValueForKeyPath:ofObject:change:context: the only value in the change dictionary for the NSKeyValueChangeKindKey I ever see is NSKeyValueChangeSetting. When I'm adding objects to the array, I expect to see NSKeyValueChangeInsertion.
Recreating my view's internal representation of the objects it observes every time I insert a single item -- particularly when I'm bulk loading hundreds of items -- presents quite a performance problem, as you'd imagine. What am I doing wrong that Cocoa seems to think I'm setting a completely new array each time I add or remove a single item?
(Note to all readers: I hate using answers for this, too, but this discussion is too long for comments. The downside, of course, is that it ends up not sorted chronologically. If you don't like it, I suggest you complain to the Stack Overflow admins about comments being length-limited and plain-text-only.)
I don't understand what you mean by implementing array accessors in the view.
Implement accessors, including indexed accessors, for the mutable array property that you've exposed as a binding.
Bindings is built on top of KVO.
And KVC.
All bindings are implemented using observeValueForKeyPath:
Overriding that is one way, sure. The other way is to implement accessors in the object with the bindable property (the view).
My custom view provides a binding that the app binds to an array -- or in this case, an array controller. Accessor methods apply to KVC, not KVO.
Cocoa Bindings will call your view's accessors for you (presumably using KVC). You don't need to implement the KVO observe method (unless, of course, you're using KVO directly).
I know this because I've done it that way. See PRHGradientView in CPU Usage.
Curiously, the documentation doesn't mention this. I'm going to file a documentation bug about it—either I'm doing something fragile or they forgot to mention this very nice feature in the docs.
It absolutely matters that I'm getting a set message on every array update. I wouldn't have posted it as a question if it didn't matter.
There are quite a large number of people who engage in something called “premature optimization”. I have no way of knowing who is one of them without asking.
I have an object that implements the indexed accessor methods for a key called contents. In those accessors, I call willChange:valuesAtIndexes:forKey: and didChange:valuesAtIndexes:forKey: when I modify the underlying array.
Don't do that. KVO posts the notifications for you when you receive a message to one of those accessors.
I also have a custom view object that is bound to contents via an NSArrayController. In observeValueForKeyPath:ofObject:change:context: the only value in the change dictionary for the NSKeyValueChangeKindKey I ever see is NSKeyValueChangeSetting. When I'm adding objects to the array, I expect to see NSKeyValueChangeInsertion.
For one thing, why are you using KVO directly? Use bind:toObject:withKeyPath:options: to bind the view's property to the array controller's arrangedObjects (I assume) property, and implement array accessors (including indexed accessors, if you like) in the view.
For another, remember that arrangedObjects is a derived property. The array controller will filter and sort its content array; the result is arrangedObjects. You could argue that permuting the indexes from the original insertion into a new insertion would be a more accurate translation of the first change into the second, but setting the entire arrangedObjects array was probably simpler to implement (something like [self _setArrangedObjects:[[newArray filteredArrayUsingPredicate:self.filterPredicate] sortedArrayUsingDescriptors:self.sortDescriptors]]).
Does it really matter? Have you profiled and found that your app is slow with wholesale array replacement?
If so, you may need to bind the view directly to the array's content property or to the original array on the underlying object, and suffer the loss of free filtering and sorting.
I call the KVO methods manually for reasons outside the scope of this issue. I have disabled automatic observing for this property. I know what I'm doing there.
I don't understand what you mean by implementing array accessors in the view. Bindings is built on top of KVO. All bindings are implemented using observeValueForKeyPath: My custom view provides a binding that the app binds to an array -- or in this case, an array controller. Accessor methods apply to KVC, not KVO.
It absolutely matters that I'm getting a set message on every array update. I wouldn't have posted it as a question if it didn't matter. I call something like
[[modelObject mutableArrayValueForKey:#"contents"] addObjectsFromArray:hundredsOfObjects];
On every insertion, my view observes a whole new array. Since I'm potentially adding hundreds of objects, that's O(N^2) when it should to be O(N). That is completely unacceptable, performance issues aside. But, since you mention it, the view does have to do a fair amount of work to observe a whole new array, which significantly slows down the program.
I can't give up using an array controller because I need the filtering and sorting, and because there's an NSTableView bound to the same controller. I rely on it to keep the sorting and selections in sync.
I solved my problem with a complete hack. I wrote a separate method that calls the KVO methods manually so that only one KVO message is sent. It's a hack, I don't like it, and it still makes my view reread the entire array -- although only once, now -- but it works for now until I figure out a better solution.